It is currently March 28th, 2024, 10:43 am

Working with date / time in Lua

Discuss the use of Lua in Script measures.
Post Reply
User avatar
jsmorley
Developer
Posts: 22628
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Working with date / time in Lua

Post by jsmorley »

One of the things that Lua is particularly good at is working with dates and times This can be valuable when you are parsing the <pubDate> from an RSS feed, or pretty much any other date/time string that you get from anywhere, and want to know how far in the past or the future the value is.
3.png
While there are good ways turn date/time strings into Windows timestamps in Rainmeter, using the Time measure, this can entail a ton of Time and Calc measures to deal with multiple date/time strings you might receive. Let's take a look at the basics of working with dates and times in Lua, and a couple of examples of how you might use this.

The basics: os.time() and os.date()

These are the two functions that do all the work with date and time in Lua. While the names are a bit confusing, in fact they both have to do with both date and time.

They are complementary. At its most basic, os.time() is used to "get or set" a date/time as a timestamp, based on a table of units, and os.date() is used to "format or break into a table of units" a date/time based on a timestamp. That is how they are in a sense "complementary". os.time() can use a table created by os.date() as its input, and os.date() can use a timestamp created by os.time() as its input.

So:

os.time() requires a {table} of units like year, month, day, hour, minute, second and returns a unix timestamp value.
os.date() requires a unix timestamp, and returns a {table} of units like year, month, day, hour, minute, second.


os.time()

First off, it is important to know that in Lua, all timestamps are based on the unix EPOCH time. That is defined as the number of seconds since 00:00:00 Coordinated Universal Time (UTC), Thursday, 1 January 1970, minus the number of leap seconds that have taken place since then. Timestamps before 1970 will be negative. Don't trust results before about 1901.

The input to os.time() is a {table} of one or more of the following table index names:

year (4 digit year)
month (month 0-12)
day (1-31)
hour (24-hour 0-23)
min (minute 0-59)
sec (second 0-59)
isdst (daylight saving time, boolean true or false)

Example:
myTimeStamp = os.time({year=2001, month=9, day=11, hour=13, min=46})
Would return a unix EPOC timestamp of 1000215960, reflecting Tuesday, September 11, 2001 13:46:00 UTC

Current date / time

While we will get into the details of os.date() in a second, be aware that there are two "shortcuts" to get the current date and time into an os.time() function to return the current date / time as a timestamp.

currentLocalTime = os.time(os.date('*t'))
currentUTCTime = os.time(os.date('!*t'))

Note the ! in the second one...

This works because as noted earlier, the two functions are complementary. os.date() returns a table, and os.time() requires a table. All will become clear when we talk about os.date().

These will return either the timestamp of the current local time on your system, or of the current UTC time. In addition simply using:
currentLocalTime = os.time() will default to returning the current local time on your system.

Math with os.time()

Now that we have a unix EPOCH timestamp, a number of seconds, we can easily do "math" with that number.

inAnHour = os.time(os.date('*t')) + 3600
tomorrowSameTime = os.time(os.date('*t')) + 86400
secondsSince911=os.time(os.date('*!t')) - os.time({year=2001, month=9, day=11, hour=13, min=46})


os.date()

The os.date() function has two purposes:

1) Return a formatted string using the current time or a timestamp as input. Used to display a date / time.
2) Return a {table} of units like year, month, day, hour, minute, second using the current local or UTC time or a timestamp as input. Generally used as input to an os.time() function.

1) Formatting an os.date() string

In order to use os.date() to format a date / string, call the function with the first parameter being a 'mask string' that uses pretty much the same format codes as the Time measure in Rainmeter. These are:

Code: Select all

%a		Abbreviated weekday name. (e.g. "Sat")
%A		Full weekday name. (e.g. "Saturday")
%b		Abbreviated month name. (e.g. "Dec")
%B		Full month name. (e.g. "December")
%c		Abbreviated date and time for system locale. (e.g. "Sat Dec 26 22:55:03 2015")
%#c		Long date and time for system locale. (e.g. "Saturday, December 26, 2015, 22:55:03")
%C		Century. Year divided by 100 and truncated to integer. (00 - 99)
%d		Day of month as number, zero padded (01 - 31).
%D		Short MM/DD/YY date, equivalent to %m/%d/%y (e.g. "12/26/15")
%e		Day of month as number, space padded. ( 1 - 31).
%F		Short YYYY-MM-DD date, equivalent to %Y-%m-%d (e.g. "2015-12-26")
%g		Week-based year, last two digits. (00 - 99)
%G		Week-based year. (e.g. "2015")
%h		Abbreviated month name, equivalent to %b. (e.g. "Dec")
%H		Hour in 24-hour format (00 - 23).
%I		Hour in 12-hour format (01 - 12).
%j		Day of year as number (001 - 366).
%m		Month as number (01 - 12).
%M		Minute as number (00 - 59).
%n		Newline character. (\n)
%p		AM/PM indicator for 12-hour clock.
%r		Full 12-hour clock time. (e.g. "10:55:03 pm")
%R		24-hour HH:MM clock time. (e.g. "22:55")
%S		Second as number (00 - 59).
%T		ISO 8601 HH:MM:SS time format, equivalent to %H:%M:%S. (e.g. 22:55)
%u		ISO 8601 weekday as number with Monday as first day of week. (1 - 7)
%U		Week of year number, with the first Sunday as first day of week one. (00 - 53)
%V		ISO 8601 week of year number (00 - 53)
%w		Weekday as number with Sunday as first day of week. (0 - 6)
%W		Week of year as number, with first Monday as first day of week one. (00 - 53)
%x		Short date representation for system locale. (e.g. "12/26/15")
%#x		Long date representation for system locale. (e.g. "Saturday, December 26, 2015")
%X		Time representation for system locale. (e.g. "22:55:03")
%y		Year without century (00 - 99).
%Y		Year with century.
%z		ISO 8601 offset from UTC in timezone for system. (e.g. "-0500")
%Z		Time zone name for system. (e.g. "Eastern Standard Time")

Note that unfortunately, Lua does NOT support the "#" character in codes to eliminate leading zeros on values that return them. You can use outputString = inputString:gsub(' 0','') to reproduce this effect.
The second parameter, completely optional, is a unix EPOCH timestamp number representing a date and time. The default if it is not used is the current local system date and time.

Examples:
prettyNow = os.date('%A, %B %d %Y at %I:%M:%S %p')
Might return Saturday, January 09 2018 at 12:42:19 AM.
pretty911 = os.date('%A, %B %d %Y at %H:%M UTC', 1000215960)
Would return Tuesday, September 11 2001 at 13:46 UTC.
prettyDerived911 = os.date('%A, %B %d %Y at %H:%M UTC', os.time({year=2001, month=9, day=11, hour=13, min=46}))
Would return Tuesday, September 11 2001 at 13:46 UTC.

While the default for the second timestamp parameter is the current local time, the current UTC time can also be used:
prettyNowUTC = os.date('%A, %B %d %Y at %I:%M:%S %p', os.time(os.date('!*t')))

2) Returning a table with os.date()

The other primary function of os.date() is to return a populated {table} of the current local or UTC date / time that can be used as input to os.time(). This is important, as any time you want to do "math" on a timestamp, any time you want to know "how long since" or "how long until", the first thing you need is "now" as a timestamp number.

This is done with two special mask strings on the os.date() function:

os.date('*t')
Returns a {table} representing the current local date / time.
os.date('!*t')
Returns a {table} representing the current UTC date / time.

In addition, os.date() can be used with an optional timestamp value as a second parameter, which will return the same table with the values for the defined timestamp. If the '*t' mask is used, the value is automatically adjusted to reflect your local offset from UTC and any DST settings. If the '!*t' mask is used, the value is treated at UTC and DST is ignored.

Again, the table values that are returned are:

year (4 digit year)
month (month 0-12)
day (1-31)
hour (24-hour 0-23)
min (minute 0-59)
sec (second 0-59)
isdst (daylight saving time, boolean true or false)
in addition, two other table fields are populated. They are not used with os.time() but can be useful for other purposes:
yday (day of the year 1-366)
wday (day of the week 1-7 - Sunday is 1)

You can use the table created as input to os.time() as:
nowTimestamp = os.time(os.date('*t'))
Returns a unix EPOC timestamp of the current local time.

And you can use the elements of the table for other purposes:

Code: Select all

nowTable = os.date('*t')
nowYear = nowTable.year
nowHour = nowTable.hour
Note that tableName.indexName is a standard Lua table "shortcut", and is the same as nowHour = nowTable['hour'].

That's the basics of how os.time() and os.date() work in Lua. In our next post, we are going to look at some example skins that use them. Stay tuned...
User avatar
jsmorley
Developer
Posts: 22628
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Examples of using date / time in Lua

Post by jsmorley »

So now you understand the basics of os.time() and os.date() in Lua, right?

For the quickest of reviews, just remember that:

os.time() requires a {table} of units like year, month, day, hour, minute, second and returns a unix EPOC timestamp number.
os.date() requires a unix EPOC timestamp number, and returns either a formatted string, or a {table} of units like year, month, day, hour, minute, second.

Let's look at a couple of skins and .lua code that demonstrates how this all works. They are not "practical" skins really, but by understanding what it going on with them, you can really wrap your head around these concepts.

Most of the explanation for what is being done and why are included in -- comments in the .lua code. Load the skin(s) and then open the .lua code for that skin to follow along. Feel free to reference back to the first post in this thread if you need to refresh yourself on exactly how the functions work in Lua.

Download the .rmskin
LuaDateTime_1.0.rmskin
(5.05 KiB) Downloaded 540 times
LuaDateParse.ini / LuaDateParse.lua

This skin uses Inline Lua section variables to call some functions in the Lua code that will "parse" a formatted date/time string into the separate elements, and turn them into UTC timestamps.

This is pretty much always going to be the first step. You are going to get strings like 2017-12-17T06:07:36Z or 2017-12-31T18:02:29-05:00 or Monday, June 15, 2009 8:45:30 PM in your skins, from WebParser or RunCommand or FileView or QuotePlugin or other sources, and before you can use them to do anything, they need to be a timestamp.

This skin just shows a variety of date/time formatted string you might get, and how to parse them and properly turn them into timestamps. Then we turn them back into pretty formatted strings and display them.

LuaDateParse.ini:

Code: Select all

[Rainmeter]
Update=1000
DynamicWindowSize=1
AccurateText=1

[Lua]
Measure=Script
ScriptFile=LuaDateParse.lua
UpdateDivider=-1

[MeterFormat1]
Meter=String
W=310
FontSize=11
FontWeight=400
FontColor=255,255,255,255
SolidColor=47,47,47,255
Padding=5,5,5,5
AntiAlias=1
Text=Sun Jan  7 09:42:54 2018#CRLF#[&Lua:Format1('Sun Jan  7 09:42:54 2018')]
DynamicVariables=1

[MeterFormat2]
Meter=String
Y=5R
W=310
FontSize=11
FontWeight=400
FontColor=255,255,255,255
SolidColor=47,47,47,255
Padding=5,5,5,5
AntiAlias=1
Text=Sun Jan  7 09:42:54 2018#CRLF#[&Lua:Format2('Sun Jan  7 09:42:54 2018')]
DynamicVariables=1

[MeterFormat3]
Meter=String
Y=5R
W=310
FontSize=11
FontWeight=400
FontColor=255,255,255,255
SolidColor=47,47,47,255
Padding=5,5,5,5
AntiAlias=1
Text=2017-12-31T18:02:29-05:00#CRLF#[&Lua:Format3('2017-12-31T18:02:29-05:00')]
DynamicVariables=1

[MeterFormat4]
Meter=String
Y=5R
W=310
FontSize=11
FontWeight=400
FontColor=255,255,255,255
SolidColor=47,47,47,255
Padding=5,5,5,5
AntiAlias=1
Text=Monday, June 15, 2009 8:45:30 PM#CRLF#[&Lua:Format4('Monday, June 15, 2009 8:45:30 PM')]
DynamicVariables=1
LuaDateParse.lua:

Code: Select all

function Initialize()

	monthShortTable={Jan=1,Feb=2,Mar=3,Apr=4,May=5,Jun=6,Jul=7,Aug=8,Sep=9,Oct=10,Nov=11,Dec=12}
	monthFullTable={January=1,February=2,March=3,April=4,May=5,June=6,July=7,August=8,September=9,October=10,November=11,December=12}
	
	-- All timestamps in Lua are in Unix EPOCH format, which is defined as:
	-- The number of seconds since 00:00:00 Coordinated Universal Time (UTC), Thursday, 1 January 1970,
	--	minus the number of leap seconds that have taken place since then.
	
	-- The full list of date / time format codes for os.date() are available here:
	-- http://www.cplusplus.com/reference/ctime/strftime/
	
	-- Information about Lua's Pattern Matching (similar, but NOT the same as PCRE regular expression) is here:
	-- http://lua-users.org/wiki/PatternsTutorial
	
	-- Note that when we create a timestamp with os.time() and it is a UTC time, we set it with isdst=false in
	-- the input table to os.time(). UTC never follows or considers Daylight Saving Time. If we don't set a value
	-- for isdst in our os.time() table, it will use the current value for that on your local system. That is not going
	-- to be correct 1/2 of the year when creating or comparing UTC date / time values.
	
end

function Format1(timeString)

	-- timeString = 'Sun Jan  7 09:42:54 2018'
	-- This has no definition of time zone, Let's assume that it is in UTC.
	-- Note that the os.time() function requires a table that is all numbers, so we
	-- need to use the monthShortTable table defined in Initialize() to turn the
	-- "Jan" into 1.
	formatPattern = '^%a+%s+(%a+)%s+(%d+)%s+(%d+):(%d+):(%d+)%s+(%d+)$'
	monthText, day, hour, min, sec, year = timeString:match(formatPattern)
	month=monthShortTable[monthText]
	
	timeStamp = os.time({month=month, day=day, year=year, hour=hour, min=min, sec=sec, isdst=false})
	
	formatedString = os.date('%A, %B %d, %Y at %I:%M:%S %p', timeStamp)
	-- Just for cosmetics, strip off any leading zeros on the day, and on the hour.
	-- Regrettably, Lua's os.date() doesn't  support Rainmeter's "#" character to suppress leading zeros.
	formatedString = formatedString:gsub(' 0',' ')
	
	return formatedString..'\nUnix timestamp is '..timeStamp
	
end

function Format2(timeString)

	-- timeString = 'Sun Jan  7 09:42:54 2018'
	-- This is the same string as our first example, but let's assume that it is in local time.
	-- We leave off any isdst value, so our local Daylight Saving Time is considered.
	formatPattern = '^%a+%s+(%a+)%s+(%d+)%s+(%d+):(%d+):(%d+)%s+(%d+)$'
	monthText, day, hour, min, sec, year = timeString:match(formatPattern)
	month=monthShortTable[monthText]
	
	timeStamp = os.time({month=month, day=day, year=year, hour=hour, min=min, sec=sec})
	
	-- Let's convert that local time to UTC time, which can help in doing date math comparisons elsewhere.
	-- To convert to UTC time, we find the difference in seconds between the current local and utc times.
	-- os.date('*t') returns the current local time and os.date('!*t') returns the current utc time.
	-- That will be the "offset" in seconds. Simply subtract that from our timeStamp, and we
	-- have converted that local time to UTC time.
	offsetSeconds = os.time(os.date('*t')) - os.time(os.date('!*t'))
	timeStamp = timeStamp - offsetSeconds
	
	formatedString = os.date('%A, %B %d, %Y %I:%M:%S %p', timeStamp)
	formatedString = formatedString:gsub(' 0',' ')
	
	return formatedString..'\nUnix timestamp is '..timeStamp
	
end

function Format3(timeString)

	-- timeString = '2017-12-31T18:02:29-05:00'
	-- This has a defined timezone offset in +/- hours:minutes at the end.
	-- We will subtract or add that offset in seconds to the time to get UTC time.
	formatPattern = '^(%d+)-(%d+)-(%d+)T(%d+):(%d+):(%d+)(.)(%d+):(%d+)$'
	year, month, day, hour, min, sec, offsetDirection, offsetHours, offsetMinutes = timeString:match(formatPattern)

	timeStamp = os.time({month=month, day=day, year=year, hour=hour, min=min, sec=sec, isdst=false})
	
	-- Convert the offsetHours and offsetMinutes into seconds.
	-- Then based on the offsetDirection, either add or subtract those seconds to
	-- get the UTC time to use in date math comparisons. If the timezone offset is
	-- negative, you are "behind" UTC time and need to add the value. If the timezone
	-- offset is positive, you are "ahead of" UTC time and need to subtract the value.
	offsetTotal = (offsetHours + (offsetMinutes * 60)) * 3600
	if offsetDirection == '-' then
		timeStamp = timeStamp + offsetTotal
	else
		timeStamp = timeStamp - offsetTotal
	end
	
	formatedString = os.date('%A, %B %d, %Y %I:%M:%S %p', timeStamp)
	formatedString = formatedString:gsub(' 0',' ')
	
	return formatedString..'\nUnix timestamp is '..timeStamp
	
end

function Format4(timeString)

	-- timeString = 'Monday, June 15, 2009 8:45:30 PM'
	-- There is no indication of time zone. Let's assume it's in local time.
	-- Note that in general, any date you get that is in 12-hour format will be a local time. UTC
	-- time is almost never expressed in 12-hour time.	
	-- This is in 12-hour time, with the trailing meridiem AM/PM indicator. We need to
	-- convert this to 24-hour time to use it with os.time() and do proper date/time math with it.
	-- In addition, note that the month is again text, but using the monthFullTable format. We 
	-- will look it up to change "June" to 6.
	formatPattern = '^%a+,%s+(%a+)%s+(%d+),%s+(%d+)%s+(%d+):(%d+):(%d+)%s+(%a+)$'
	monthText, day, year, hour, min, sec, meridiem = timeString:match(formatPattern)
	month=monthFullTable[monthText]

	if (string.upper(meridiem) == 'AM') and (tonumber(hour) == 12) then
		hour = 0
	elseif (string.upper(meridiem) == 'PM') and (tonumber(hour) < 12) then
		hour = hour + 12
	end

	timeStamp = os.time({month=month, day=day, year=year, hour=hour, min=min, sec=sec})
	
	-- As we did before, let's convert this to UTC time.
	offsetSeconds = os.time(os.date('*t')) - os.time(os.date('!*t'))
	timeStamp = timeStamp - offsetSeconds
	
	formatedString = os.date('%A, %B %d, %Y %I:%M:%S %p', timeStamp)
	formatedString = formatedString:gsub(' 0',' ')
	
	return formatedString..'\nUnix timestamp is '..timeStamp
	
end
2.png

LuaDateCompare.ini / LuaDateCompare.lua

This skin uses Inline Lua section variables to repeatedly use a function in the Lua code to "parse" formatted date/time strings into the separate elements, and turn them into UTC timestamps. Note, the ability to re-use a single function multiple times in your skin is the power of Lua in a nutshell!

It will then compare these timestamps to the current UTC time, and display the resulting difference as "days, hours, minutes and seconds".

LuaDateCompare.ini:

Code: Select all

[Rainmeter]
Update=1000
DynamicWindowSize=1
AccurateText=1

[Lua]
Measure=Script
ScriptFile=LuaDateCompare.lua
UpdateDivider=-1

[MeterElapsed1]
Meter=String
W=360
FontSize=11
FontWeight=400
FontColor=255,255,255,255
SolidColor=47,47,47,255
Padding=5,5,5,5
AntiAlias=1
Text=[&Lua:ElapsedTime('2018-01-09T15:57:00Z')]
DynamicVariables=1

[MeterElapsed2]
Meter=String
Y=5R
W=360
FontSize=11
FontWeight=400
FontColor=255,255,255,255
SolidColor=47,47,47,255
Padding=5,5,5,5
AntiAlias=1
Text=[&Lua:ElapsedTime('2018-03-15T13:02:40Z')]
DynamicVariables=1

[MeterElapsed3]
Meter=String
Y=5R
W=360
FontSize=11
FontWeight=400
FontColor=255,255,255,255
SolidColor=47,47,47,255
Padding=5,5,5,5
AntiAlias=1
Text=[&Lua:ElapsedTime('2017-09-22T16:22:34Z')]
DynamicVariables=1
LuaDateCompare.lua:

Code: Select all

function ElapsedTime(timeString)

	-- timeString = '2018-01-09T15:57:00Z'
	-- First, let's get a timestamp for the input timeString. You can check the
	-- LuaDateParse.lua file to see some other examples of parsing date / time strings.
	-- Note that we set isdst=false, as we want to compare a fixed UTC time, which never
	-- follows DST, to the current UTC time, which is also never in DST. If we set an 
	-- os.time() value to a table that doesn't include a value for isdst, the value for isdst
	-- currently on your local system will be used. 
	formatPattern = '^(%d+)-(%d+)-(%d+)T(%d+):(%d+):(%d+)Z$'
	year, month, day, hour, min, sec = timeString:match(formatPattern)
	thenStamp = os.time({month=month, day=day, year=year, hour=hour, min=min, sec=sec, isdst=false})

	-- Create a formatted date / time string followed by a linefeed, to put at the beginning.
	preString = os.date('%A, %B %d, %Y at %I:%M:%S  %p', thenStamp)..'\n'
	-- Just for cosmetics, strip off any leading zeros on the day, and on the hour.
	-- Regrettably, Lua's os.date() doesn't  support Rainmeter's "#" character to suppress leading zeros.
	preString = preString:gsub(' 0',' ')
	
	-- Now let's find out how many elapsed seconds have passed or remain until the input date / time,
	-- by subtracting that timestamp from the current UTC time.
	nowStamp = os.time(os.date('!*t'))
	elapsedSeconds = nowStamp - thenStamp
	-- Note that if the date is in the future, the difference will be negative. Using math.abs() causes
	-- the difference in seconds, which are the same in either case, to always be a positive number.
	if thenStamp > nowStamp then postString = ' remaining' else postString = ' elapsed' end	
	elapsedSeconds = math.abs(elapsedSeconds)
	
	-- Call the ConvertSeconds() function to turn those elapsed seconds into a table
	-- of the days, hours, minutes and seconds. We will call that table "et", just to 
	-- keep the name short.
	et = ConvertSeconds(elapsedSeconds)
	
	-- You don't want to say "1 minutes", but rather "1 minute"
	if et.days == 1 then daysText = ' day, ' else daysText = ' days, ' end
	if et.hours == 1 then hoursText = ' hour, ' else hoursText = ' hours, ' end
	if et.mins == 1 then minsText = ' minute, ' else minsText = ' minutes, ' end
	if et.secs == 1 then secsText = ' second' else secsText = ' seconds' end
	
	-- Create the final output string.
	-- Start with the formatted date / time string created above, with the linefeed, followed by
	-- The days, hours, minutes and seconds. Don't show more than you need to. If there are 
	-- no days, there is no need to have "0 days" at the beginning. Stick postString at the end.
	if et.days > 0 then
		outString = preString..et.days..daysText..et.hours..hoursText..et.mins..minsText..et.secs..secsText..postString
	elseif et.hours > 0 then
		outString = preString..et.hours..hoursText..et.mins..minsText..et.secs..secsText..postString
	elseif et.mins > 0 then
		outString = preString..et.mins..minsText..et.secs..secsText..postString
	else
		outString = preString..et.secs..secsText..postString
	end
	
	return outString
	
end

function ConvertSeconds(secondsArg)
    
	local daysDiff = math.floor(secondsArg / 86400)
	local remainder = (secondsArg % 86400)
	local hoursDiff = math.floor(remainder / 3600)
	local remainder = (secondsArg % 3600)
	local minsDiff = math.floor(remainder / 60)
	local secsDiff = (remainder % 60)
	
	local elapsedTable = {days=daysDiff, hours=hoursDiff, mins=minsDiff, secs=secsDiff}	
	
	return elapsedTable
	
end
1.png
Hopes this helps with seeing how this date / time stuff can be used in a skin. In our next installment, we will look at at a more "practical" example that parses an RSS news feed, and displays how "old" each item is in the skin. Stay tuned...
User avatar
jsmorley
Developer
Posts: 22628
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Example Skin to finish it off

Post by jsmorley »

So we have reached the end of our guide... Now you are an expert on using date and time in Lua!

Here is a more "practical" example skin, that gets the latest five items from the Google New rss feed, and displays the age of each item in days, hours, minutes and seconds both in the displayed meter and the tooltip.

Download the .rmskin
GoogleNews_1.0.rmskin
(23.49 KiB) Downloaded 315 times
1.png
GoogleNews.ini:

Code: Select all

[Rainmeter]
Update=1000
DynamicWindowSize=1
AccurateText=1

[Metadata]
Name=GoogleNews
Author=JSMorley
Information=Demonstrates parsing and returning date / time information from Lua
Version=Jan 10, 2018
License=Creative Commons Attribution-Non-Commercial-Share Alike 3.0

[Variables]
GoogleGreen=60,186,84,255
GoogleYellow=244,194,13,255
GoogleRed=219,50,54,255
GoogleBlue=72,133,237,255
OddColor=27,27,27,255
EvenColor=10,10,10,255
URL=https://news.google.com/news/rss?ned=us&gl=US&hl=en
Item=.*<item>.*<title>(.*)</title>.*<link>(.*)</link>.*<pubDate>(.*)</pubDate>.*</item>

; Measures

[Lua]
Measure=Script
ScriptFile=ReturnAge.lua
UpdateDivider=-1

[MeasureSite]
Measure=Plugin
Plugin=WebParser
URL=#URL#
RegExp=(?siU)#Item##Item##Item##Item##Item#
UpdateRate=120
FinishAction=[!ShowMeterGroup Items]

[MeasureTitle1]
Measure=Plugin
Plugin=WebParser
URL=[MeasureSite]
StringIndex=1

[MeasureLink1]
Measure=Plugin
Plugin=WebParser
URL=[MeasureSite]
StringIndex=2

[MeasureDate1]
Measure=Plugin
Plugin=WebParser
URL=[MeasureSite]
StringIndex=3

[MeasureTitle2]
Measure=Plugin
Plugin=WebParser
URL=[MeasureSite]
StringIndex=4

[MeasureLink2]
Measure=Plugin
Plugin=WebParser
URL=[MeasureSite]
StringIndex=5

[MeasureDate2]
Measure=Plugin
Plugin=WebParser
URL=[MeasureSite]
StringIndex=6

[MeasureTitle3]
Measure=Plugin
Plugin=WebParser
URL=[MeasureSite]
StringIndex=7

[MeasureLink3]
Measure=Plugin
Plugin=WebParser
URL=[MeasureSite]
StringIndex=8

[MeasureDate3]
Measure=Plugin
Plugin=WebParser
URL=[MeasureSite]
StringIndex=9

[MeasureTitle4]
Measure=Plugin
Plugin=WebParser
URL=[MeasureSite]
StringIndex=10

[MeasureLink4]
Measure=Plugin
Plugin=WebParser
URL=[MeasureSite]
StringIndex=11

[MeasureDate4]
Measure=Plugin
Plugin=WebParser
URL=[MeasureSite]
StringIndex=12

[MeasureTitle5]
Measure=Plugin
Plugin=WebParser
URL=[MeasureSite]
StringIndex=13

[MeasureLink5]
Measure=Plugin
Plugin=WebParser
URL=[MeasureSite]
StringIndex=14

[MeasureDate5]
Measure=Plugin
Plugin=WebParser
URL=[MeasureSite]
StringIndex=15

; Meters

[MeterBackground]
Meter=Shape
Shape=Rectangle 0.5,0.5,300,435,12 | Fill Color 0,0,0,255 | StrokeWidth 1.5 | Stroke Color 170,170,170,255
X=0
UpdateDivider=-1

[MeterHeaderIcon]
Meter=Image
ImageName=#@#Images\GoogleNews.png
X=15
Y=5
W=56
PreserveAspectRatio=1

[MeterHeader]
Meter=String
X=84
Y=12
FontFace=Product Sans
FontSize=25
FontWeight=400
FontColor=#GoogleBlue#
AntiAlias=1
InlineSetting=Color | #GoogleBlue#
InlinePattern=^(G)oogle News
InlineSetting2=Color | #GoogleRed#
InlinePattern2=^G(o)ogle News
InlineSetting3=Color | #GoogleYellow#
InlinePattern3=^Go(o)gle News
InlineSetting4=Color | #GoogleBlue#
InlinePattern4=^Goo(g)le News
InlineSetting5=Color | #GoogleGreen#
InlinePattern5=^Goog(l)e News
InlineSetting6=Color | #GoogleRed#
InlinePattern6=^Googl(e) News
InlineSetting7= Underline
InlinePattern7=News
Text=Google News

; Items

[MeterTitle1]
Meter=String
MeasureName=MeasureTitle1
Group=Items
Hidden=1
X=10
Y=25R
W=265
FontSize=12
FontWeight=400
FontColor=255,255,255,255
Padding=5,5,5,5
SolidColor=#OddColor#
ClipString=2
ClipStringH=37
AntiAlias=1
ToolTipText=[&MeasureTitle1]#CRLF##CRLF#[&Lua:ReturnAge('[&MeasureDate1]')]
ToolTipType=1
DynamicVariables=1
LeftMouseUpAction=["[&MeasureLink1]"]

[MeterAge1]
Meter=String
Group=Items
Hidden=1
X=10
Y=0R
W=265
FontSize=10
FontWeight=400
FontColor=255,255,255,255
Padding=5,0,5,5
SolidColor=#OddColor#
AntiAlias=1
DynamicVariables=1
Text=[&Lua:ReturnAge('[&MeasureDate1]')]

[MeterTitle2]
Meter=String
MeasureName=MeasureTitle2
Group=Items
Hidden=1
X=10
Y=5R
W=265
FontSize=12
FontWeight=400
FontColor=255,255,255,255
Padding=5,5,5,5
SolidColor=#EvenColor#
ClipString=2
ClipStringH=37
AntiAlias=1
ToolTipText=[&MeasureTitle2]#CRLF##CRLF#[&Lua:ReturnAge('[&MeasureDate2]')]
ToolTipType=1
DynamicVariables=1
LeftMouseUpAction=["[&MeasureLink2]"]

[MeterAge2]
Meter=String
Group=Items
Hidden=1
X=10
Y=0R
W=265
FontSize=10
FontWeight=400
FontColor=255,255,255,255
Padding=5,0,5,5
SolidColor=#EvenColor#
AntiAlias=1
DynamicVariables=1
Text=[&Lua:ReturnAge('[&MeasureDate2]')]

[MeterTitle3]
Meter=String
MeasureName=MeasureTitle3
Group=Items
Hidden=1
X=10
Y=5R
W=265
FontSize=12
FontWeight=400
FontColor=255,255,255,255
Padding=5,5,5,5
SolidColor=#OddColor#
ClipString=2
ClipStringH=37
AntiAlias=1
ToolTipText=[&MeasureTitle3]#CRLF##CRLF#[&Lua:ReturnAge('[&MeasureDate3]')]
ToolTipType=1
DynamicVariables=1
LeftMouseUpAction=["[&MeasureLink3]"]

[MeterAge3]
Meter=String
Group=Items
Hidden=1
X=10
Y=0R
W=265
FontSize=10
FontWeight=400
FontColor=255,255,255,255
Padding=5,0,5,5
SolidColor=#OddColor#
AntiAlias=1
DynamicVariables=1
Text=[&Lua:ReturnAge('[&MeasureDate3]')]

[MeterTitle4]
Meter=String
MeasureName=MeasureTitle4
Group=Items
Hidden=1
X=10
Y=5R
W=265
FontSize=12
FontWeight=400
FontColor=255,255,255,255
Padding=5,5,5,5
SolidColor=#EvenColor#
ClipString=2
ClipStringH=37
AntiAlias=1
ToolTipText=[&MeasureTitle4]#CRLF##CRLF#[&Lua:ReturnAge('[&MeasureDate4]')]
ToolTipType=1
DynamicVariables=1
LeftMouseUpAction=["[&MeasureLink4]"]

[MeterAge4]
Meter=String
Group=Items
Hidden=1
X=10
Y=0R
W=265
FontSize=10
FontWeight=400
FontColor=255,255,255,255
Padding=5,0,5,5
SolidColor=#EvenColor#
AntiAlias=1
DynamicVariables=1
Text=[&Lua:ReturnAge('[&MeasureDate4]')]

[MeterTitle5]
Meter=String
MeasureName=MeasureTitle5
Group=Items
Hidden=1
X=10
Y=5R
W=265
FontSize=12
FontWeight=400
FontColor=255,255,255,255
Padding=5,5,5,5
SolidColor=#OddColor#
ClipString=2
ClipStringH=37
AntiAlias=1
ToolTipText=[&MeasureTitle5]#CRLF##CRLF#[&Lua:ReturnAge('[&MeasureDate5]')]
ToolTipType=1
DynamicVariables=1
LeftMouseUpAction=["[&MeasureLink5]"]

[MeterAge5]
Meter=String
Group=Items
Hidden=1
X=10
Y=0R
W=265
FontSize=10
FontWeight=400
FontColor=255,255,255,255
Padding=5,0,5,5
SolidColor=#OddColor#
AntiAlias=1
DynamicVariables=1
Text=[&Lua:ReturnAge('[&MeasureDate5]')]
ReturnAge.lua:

Code: Select all

function Initialize()

	monthShortTable={Jan=1,Feb=2,Mar=3,Apr=4,May=5,Jun=6,Jul=7,Aug=8,Sep=9,Oct=10,Nov=11,Dec=12}

end

function ReturnAge(timeString)
	
	if timeString == '' then return 'Waiting...' end
	
	-- timeString example: Wed, 10 Jan 2018 11:06:30 GMT
	formatPattern = '^%a+,%s+(%d+)%s+(%a+)%s+(%d+)%s+(%d+):(%d+):(%d+) GMT$'
	day, monthText, year, hour, min, sec = timeString:match(formatPattern)
	month=monthShortTable[monthText]

	timeStamp = os.time({month=month, day=day, year=year, hour=hour, min=min, sec=sec, isdst=false})
	elapsedSeconds = os.time(os.date('!*t')) - timeStamp

	et = ConvertSeconds(elapsedSeconds)
	
	if et.days == 1 then daysText = ' day, ' else daysText = ' days, ' end
	if et.hours == 1 then hoursText = ' hour, ' else hoursText = ' hours, ' end
	if et.mins == 1 then minsText = ' minute ' else minsText = ' minutes ' end
	if et.secs == 1 then secsText = ' second ' else secsText = ' seconds ' end
	
	if et.days > 0 then
		outString = et.days..daysText..et.hours..hoursText..et.mins..minsText..'ago'
	elseif et.hours > 0 then
		outString = et.hours..hoursText..et.mins..minsText..'ago'
	elseif et.mins > 0 then
		outString = et.mins..minsText..'ago'
	else
		outString = et.secs..secsText..'ago'
	end
	
	return outString

end

function ConvertSeconds(secondsArg)
    
	local daysDiff = math.floor(secondsArg / 86400)
	local remainder = (secondsArg % 86400)
	local hoursDiff = math.floor(remainder / 3600)
	local remainder = (secondsArg % 3600)
	local minsDiff = math.floor(remainder / 60)
	local secsDiff = (remainder % 60)
	
	local elapsedTable = {days=daysDiff, hours=hoursDiff, mins=minsDiff, secs=secsDiff}	
	
	return elapsedTable
	
end
Feel free to ask any questions you might have...
Post Reply