It is currently October 13th, 2024, 8:40 am

New Time and Locale functionality

Changes made during the Rainmeter 3.2 beta cycle.
User avatar
jsmorley
Developer
Posts: 22851
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

New Time and Locale functionality

Post by jsmorley »

We have added what we think is some significant new functionality for how you can get, use and display date and time information in Rainmeter.

These fall into a couple of categories, each with new options and option values. I will touch on each of these individually, then do a post with an example skin that should demonstrate how it all fits together.

Input - Reading and evaluating a date and time string.

TimeStamp

The first and most important new capability is that you can now define a TimeStamp option for a Time measure that consists of a date and / or time representation in just about any format.

The documentation for TimeStamp is probably the best explanation of this, but in general this means you can define something like:

Code: Select all

[MeasureTime]
Measure=Time
TimeStamp=2015-02-18T14:58:00Z
That might be hard-coded in the option, a [Variables] you set, or most likely, a date and time string returned by a WebParser measure. An <updated> value for an entry in an RSS feed for example.

Code: Select all

[MeasureTime]
Measure=Time
TimeStamp=[SomeWebParserMeasure]
DynamicVariables=1
This TimeStamp string can be ANY format you might get from a web site, or any format you define yourself. The key is that you are going to follow that TimeStamp value with another new option, that will tell the measure how to "decode" it.

TimeStampFormat

When you use a formatted string in the TimeStamp option, it will require that you also have a TimeStampFormat option that creates a "mask" for the format. In effect you tell the measure what those bits and pieces of the string mean as Time measure Format codes.

Again, the documentation for TimeStampFormat is the most complete way to understand this, but as an example you might use:

Code: Select all

[MeasureTime]
Measure=Time
TimeStamp=2015-02-18T14:58:00Z
TimeStampFormat=%Y-%m-%dT%H:%M:%S
What that does is "mask" the formatted TimeStamp string, telling it what the different components mean as Time Format codes. It can be any combination of Format codes, spaces and text that you need to create the mask that matches the string.

TimeStampLocale

One last new option to complete the "input" part of things. If the string that you get, for instance from a WebParser measure, is in a different "language / locale" than "English" which is what the Time measure uses by default, you can tell it what language / locale it is in so it will be read and decoded properly. For that we use the new TimeStampLocale option. This option consists of a NLS Language / Locale Identifiers code, that identifies the language, or language and country, desired.

For example, if we assume that the date / time string you are getting is in German, you might use:

Code: Select all

[MeasureTime]
Measure=Time
TimeStamp=Mittwoch, 18. Februar 2015 18:57:16
TimeStampFormat=%A, %#d. %m %Y %H:%M:%S
TimeStampLocale=de-DE
Input - The result

The effect of this is that any formatted date and / or time string is decoded by the measure, and will set the value of the measure to the appropriate Windows timestamp number. This is a number of seconds since January 1, 1601, and is what the Time measure uses, and has always used, to define the date and time for the measure.

This Windows timestamp number can then be used in Calc measures or other formulas to do "math" on the date, to compare the date from the measure with the current date for instance, or it can be formatted for display in a String meter, which we will now address.

Output - Formatting the display

Format

The Time measure has always used the Format option to format the Windows timestamp value of the measure into a readable date / time string that you define. This has not changed with the new functionality.

Code: Select all

[MeasureTime]
Measure=Time
TimeStamp=2015-02-18T14:58:00Z
TimeStampFormat=%Y-%m-%dT%H:%M:%S
Format=%A, %B %#d, %Y %H:%M:%S
This would display "Wednesday, February 18, 2015 14:58:00" when the value of [MeasureTime] is used in a String meter.

So what we have done is "input" a formatted date / time string, "decoded" it into a valid timestamp the measure can use, and "formatted" it any way we like for display.

So the string "2015-02-18T14:58:00Z" from our WebParser RSS feed (for instance) is turned into "Wednesday, February 18, 2015 14:58:00" in our skin. Very powerful stuff.

FormatLocale

One last new option to complete the "output" part of things. Remember above, we used the new TimeStampLocale option to tell the measure what language / locale the "input" TimeStamp string is in.

By default the "output" of the Format option will always be in "English" in a Rainmeter Time measure. So if your system's locale is other than "English", or you want to set the output to some other desired language, you can simply use the FormatLocale option, which will use the same NLS Language / Locale Identifiers codes to define the language / locale that should be used when the measure value is formatted with the Format option.

So for instance if your system's locale was "German":

Code: Select all

[MeasureTime]
Measure=Time
TimeStamp=Tuesday, January 27, 2015 at 15:22:30
TimeStampFormat=%A, %B %#d, %Y at %H:%M:%S
TimeStampLocale=en-US
FormatLocale=fr-FR
Format=%A, %B %#d, %Y %H:%M:%S
This would accept and decode the date / time string in English, and output the result in the French language and locale. Doesn't matter a bit that your system is in German. If you wanted the output in German, you would use FormatLocale=de-DE.

Output - The result

So we are now able to use any formatted date / time string in the measure, that is any format, in any language. We "decode" that string to create a Time measure value, and using the Format and FormatLocale options, output the result in any format and language we want in a String meter.

There are a couple of other new options that are somewhat related to this, that I will touch on in additional posts following this one, then we will look at an example skin that sorta pulls it all together.
User avatar
jsmorley
Developer
Posts: 22851
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Change to the UpTime measure

Post by jsmorley »

One of the things that occurred to us when we were working on the changes to the date / time functionality in the post above, is that this new functionality is going to make it pretty easy to get a date / time from some source like an RSS feed, and compare the Windows timestamp value that creates with a Time measure getting the current system or GMT time. In effect a way to ask "how old is that item?".

Any mathematical comparison of two Windows timestamp values will end up with a result in "seconds". That is as it should be, but what if you want display that as "days, hours, minutes, seconds" instead. We needed a way to facilitate that.

What we have done is add a new SecondsValue option to the UpTime measure.

If defined, this will override and change the default behavior of UpTime. Instead of getting and formatting the number of seconds since the system was last restarted, it will then use that SecondsValue number of seconds as the "input" for the measure, and allow you to format that as "days, hours, minutes, seconds" as desired using the Format option.

Code: Select all

[MeasureAge]
Measure=UpTime
SecondsValue=[SomeCalcMeasure]
Format=%4!i!d %3!i!h %2!i!m %1!i!s
Might return "2d 5h 13m 42s"
User avatar
jsmorley
Developer
Posts: 22851
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

New [SectionName:Timestamp] Section Variable

Post by jsmorley »

Another thing we found when working on the changes to the date / time functionality in the first post above, is that there is, and always has been, an annoying limitation with how a Time measure returns "number" values.

The way it works is that if there is no Format option on a Time measure, then the number value of the measure will be the Windows timestamp number representing that date / time. If there IS a Format option on the measure, then the number value will be one of two things.

First possibility is a number that the Format option results in. For instance Format=%H will return the number of "hours" at 15:00 as "15" which is a valid number and will be the number value of the measure.

Second possibility is that the Format option does not return a valid "number", like Format=%A (Wednesday). In this case, the number value of the measure will be "zero".

In either of those cases, the Windows timestamp number of the measure is in effect "lost", and can't be used in calculations or formulas. This means that often you have to create two identical Time measures, one WITH the Format option, for "display" and one WITHOUT the Format option, for "math".

We have added a new :Timestamp Section Variable modifier. This will only be valid when applied to a Time measure, and will return the Windows timestamp value of that measure whether it has a Format option on it or not. That value can then be used in calculations or formulas, without needing to create additional Time measures.

For example:

Code: Select all

[MeasureTime]
Measure=Time
TimeStamp=2015-02-18T14:58:00Z
TimeStampFormat=%Y-%m-%dT%H:%M:%S
Format=%A, %B %#d, %Y %H:%M:%S

[MeasureCalc]
Measure=Calc
Formula=([MeasureTime:Timestamp] + 86400)
DynamicVariables=1
User avatar
moshi
Posts: 1740
Joined: November 13th, 2012, 9:53 pm

Re: New Time and Locale functionality

Post by moshi »

FormatLocale still defaults to English here on my German System. (as it ever was, Rainmeter never used my local language for time measures. maybe that worked pre-XP, i don't remember)

this is what i do get local language:

Code: Select all

[MeasureLanguage]
Measure=Registry
RegHKey=HKEY_CURRENT_USER
RegKey=Control Panel\International
RegValue=LocaleName
UpdateDivider=-1

[MeasureTime]
Measure=Time
Format=%#c
FormatLocale=[MeasureLanguage]
DynamicVariables=1
User avatar
moshi
Posts: 1740
Joined: November 13th, 2012, 9:53 pm

Re: New Time and Locale functionality

Post by moshi »

i forgot: awesome update :thumbup:
User avatar
jsmorley
Developer
Posts: 22851
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Pulling it all together

Post by jsmorley »

Here is an example skin that demonstrates much of the new and changed functionality described in the first three posts in this thread.

Let's get the skin and take a look at the output, then we can tear it apart a bit and describe what it is doing:
FormatTime_1.0.rmskin
FormatTime.jpg
First let's look at the general goals and result:

We are getting the first ATOM feed <entry> from the Breaking News site at http://www.breakingnews.com/feeds/rss/. One of the bits of information we can get, and now properly use, is the <updated> date and time of the entry item.

As you can see in the first date / time we display, the feed is returning "2015-02-18T17:27:33Z" as a "string" in the feed.

So what we want to do is get and format that date and time in a nice, readable format in both the original GMT and our local time. We then want to evaluate and display how "old" that entry is.

Here is the skin code, which we will look at more closely:

Code: Select all

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

[Metadata]
Name=FormatTime
Author=JSMorley
Information=Example of using the new options and features related to getting, using and formatting date / time information in a skin.||Full explanations are at http://rainmeter.net/forum/viewtopic.php?p=108973#p108973
License=Creative Commons Attribution-Non-Commercial-Share Alike 3.0
Version=Feb 18, 2015

[Variables]
LocaleID=en-US
OuputFormat=%#c

[MeasureCurrentLocal]
Measure=Time

[MeasureCurrentGMT]
Measure=Time
TimeZone=0

[MeasureFeedSite]
Measure=Plugin
Plugin=WebParser
URL=http://www.breakingnews.com/feeds/rss/
RegExp=(?siU)<entry>.*<title>(.*)</title>.*<link.*href="(.*)".*<updated>(.*)</updated>.*
UpdateRate=30
DecodeCharacterReference=1
FinishAction=[!EnableMeasureGroup Times][!UpdateMeasureGroup Times][!ShowMeterGroup Times]

[MeasureTitle]
Measure=Plugin
Plugin=WebParser
URL=[MeasureFeedSite]
StringIndex=1

[MeasureLink]
Measure=Plugin
Plugin=WebParser
URL=[MeasureFeedSite]
StringIndex=2

[MeasureEntryUpdated]
Measure=Plugin
Plugin=WebParser
URL=[MeasureFeedSite]
StringIndex=3

[MeasureGMTUpdated]
Measure=Time
Group=Times
TimeStamp=[MeasureEntryUpdated]
TimeStampFormat=%Y-%m-%dT%H:%M:%S
TimeStampLocale=#LocaleID#
FormatLocale=#LocaleID#
Format=#OuputFormat#
DynamicVariables=1
Disabled=1
UpdateDivider=-1

[MeasureTimeDiff]
Measure=Calc
Group=Times
Formula=Abs(([MeasureGMTUpdated:Timestamp] - MeasureCurrentGMT))
DynamicVariables=1
Disabled=1
IfCondition=MeasureTimeDiff < 60
IfTrueAction=[!SetOption MeasureFormatDiff Format "%1!i!s"]
IfCondition2=(MeasureTimeDiff >= 60) && (MeasureTimeDiff < 3600)
IfTrueAction2=[!SetOption MeasureFormatDiff Format "%2!i!m"]
IfCondition3=(MeasureTimeDiff >= 3600) && (MeasureTimeDiff < 86400)
IfTrueAction3=[!SetOption MeasureFormatDiff Format "%3!i!h %2!i!m"]
IfCondition4=MeasureTimeDiff >= 86400
IfTrueAction4=[!SetOption MeasureFormatDiff Format "%4!i!d %3!i!h %2!i!m"]

[MeasureFormatDiff]
Measure=UpTime
Group=Times
SecondsValue=[MeasureTimeDiff]
Format="%4!i!d %3!i!h %2!i!m %1!i!s"
DynamicVariables=1
Disabled=1

[MeasureLocalUpdated]
Measure=Time
Group=Times
TimeStamp=([MeasureCurrentLocal:] - [MeasureTimeDiff:])
TimeStampLocale=#LocaleID#
FormatLocale=#LocaleID#
Format=#OuputFormat#
DynamicVariables=1
Disabled=1
UpdateDivider=-1

[MeterTitle]
Meter=String
Group=Times
MeasureName=MeasureTitle
W=350
ClipString=2
FontSize=11
FontColor=235,245,255,255
SolidColor=30,40,50,255
Padding=5,5,5,5
AntiAlias=1
DynamicVariables=1
Hidden=1
LeftMouseUpAction=["[MeasureLink]"]

[MeterGMTString]
Meter=String
Group=Times
MeasureName=MeasureEntryUpdated
Y=5R
FontSize=11
FontColor=235,245,255,255
SolidColor=30,40,50,255
Padding=5,5,5,5
AntiAlias=1
Hidden=1
Text=Updated String: %1

[MeterGMTFormatted]
Meter=String
Group=Times
MeasureName=MeasureGMTUpdated
Y=5R
FontSize=11
FontColor=235,245,255,255
SolidColor=30,40,50,255
Padding=5,5,5,5
AntiAlias=1
Hidden=1
Text=Feed Time: %1

[MeterLocalFormatted]
Meter=String
Group=Times
MeasureName=MeasureLocalUpdated
Y=5R
FontSize=11
FontColor=235,245,255,255
SolidColor=30,40,50,255
Padding=5,5,5,5
AntiAlias=1
Hidden=1
Text=Local Time: %1

[MeterEntryAge]
Meter=String
Group=Times
MeasureName=MeasureFormatDiff
Y=5R
FontSize=11
FontColor=235,245,255,255
SolidColor=30,40,50,255
Padding=5,5,5,5
AntiAlias=1
Hidden=1
Text=Entry Age: %1
So first we get the current local and GMT time for our system. We will use those later in some calculations.

Code: Select all

[MeasureCurrentLocal]
Measure=Time

[MeasureCurrentGMT]
Measure=Time
TimeZone=0
Next, we go out with WebParser and get the first <entry> for the feed. We create normal WebParser child measures for the "title / link / updated" values.

Code: Select all

[MeasureFeedSite]
Measure=Plugin
Plugin=WebParser
URL=http://www.breakingnews.com/feeds/rss/
RegExp=(?siU)<entry>.*<title>(.*)</title>.*<link.*href="(.*)".*<updated>(.*)</updated>.*
UpdateRate=30
DecodeCharacterReference=1
FinishAction=[!EnableMeasureGroup Times][!UpdateMeasureGroup Times][!ShowMeterGroup Times]

[MeasureTitle]
Measure=Plugin
Plugin=WebParser
URL=[MeasureFeedSite]
StringIndex=1

[MeasureLink]
Measure=Plugin
Plugin=WebParser
URL=[MeasureFeedSite]
StringIndex=2

[MeasureEntryUpdated]
Measure=Plugin
Plugin=WebParser
URL=[MeasureFeedSite]
StringIndex=3
Ok, here is where the fun starts.

Code: Select all

[MeasureGMTUpdated]
Measure=Time
Group=Times
TimeStamp=[MeasureEntryUpdated]
TimeStampFormat=%Y-%m-%dT%H:%M:%S
TimeStampLocale=#LocaleID#
FormatLocale=#LocaleID#
Format=#OuputFormat#
DynamicVariables=1
Disabled=1
UpdateDivider=-1
The goal of this measure is to turn that <updated> string from the feed, remember it was something like "2015-02-18T17:27:33Z", into a valid Time measure we can use.

TimeStamp=[MeasureEntryUpdated]
This will use the value of the WebParser child measure as the input to the TimeStamp option.
TimeStampFormat=%Y-%m-%dT%H:%M:%S
This sets up the "mask", that allows the measure to "decode" that string from the feed.
TimeStampLocale=#LocaleID#
This tells the measure what language / locale the feed string is in. We set that to "en-US" (US English) in the [Variables] section at the beginning of the skin.
FormatLocale=#LocaleID#
This tells the measure that the output "Format" should also be in US English. We could have set this to French or Russian or any other language we wanted.
Format=#OuputFormat#
This is just the normal Format option for a time measure, which we set in [Variables] to "%#c" or "Long date and time representation for system or defined locale." That will give us the formatted output we want when used in a String meter.

Next, we are going to figure out how "old" this entry is. Let's look at the first part of the Calc measure initially:

Code: Select all

[MeasureTimeDiff]
Measure=Calc
Group=Times
Formula=Abs(([MeasureGMTUpdated:Timestamp] - MeasureCurrentGMT))
DynamicVariables=1
Disabled=1
What we are doing is getting the difference between the Windows timestamp from the ATOM feed, and the current GMT time. We use the Abs() function to ensure it is a positive number, so we can use it later as the "age" of the post.

Then:

Code: Select all

IfCondition=MeasureTimeDiff < 60
IfTrueAction=[!SetOption MeasureFormatDiff Format "%1!i!s"]
IfCondition2=(MeasureTimeDiff >= 60) && (MeasureTimeDiff < 3600)
IfTrueAction2=[!SetOption MeasureFormatDiff Format "%2!i!m"]
IfCondition3=(MeasureTimeDiff >= 3600) && (MeasureTimeDiff < 86400)
IfTrueAction3=[!SetOption MeasureFormatDiff Format "%3!i!h %2!i!m"]
IfCondition4=MeasureTimeDiff >= 86400
IfTrueAction4=[!SetOption MeasureFormatDiff Format "%4!i!d %3!i!h %2!i!m"]

[MeasureFormatDiff]
Measure=UpTime
Group=Times
SecondsValue=[MeasureTimeDiff]
Format="%4!i!d %3!i!h %2!i!m %1!i!s"
DynamicVariables=1
Disabled=1
At the end of that [MeasureTimeDiff] measure above, we are examining the result and using !SetOption to set the Format option of an UpTime measure to show the result in "days, hours, minutes, seconds" as needed, but only when needed. If its only 120 seconds old, we only need "minutes, seconds".

Ok, so we have the formatted date / time in GMT, and we know how "old" it is, let's turn that GMT time into our local time:

Code: Select all

[MeasureLocalUpdated]
Measure=Time
Group=Times
TimeStamp=([MeasureCurrentLocal:] - [MeasureTimeDiff:])
TimeStampLocale=#LocaleID#
FormatLocale=#LocaleID#
Format=#OuputFormat#
DynamicVariables=1
Disabled=1
UpdateDivider=-1
What we are doing here is just subtracting that "age" in seconds from our local time, and formatting it exactly the way we did the GMT time.

That's really it. Now we just set up a few meters to display all those results, and we get out output in the image above.

Let me know if there are any questions.
You do not have the required permissions to view the files attached to this post.
User avatar
jsmorley
Developer
Posts: 22851
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: New Time and Locale functionality

Post by jsmorley »

moshi wrote:FormatLocale still defaults to English here on my German System. (as it ever was, Rainmeter never used my local language for time measures. maybe that worked pre-XP, i don't remember)

this is what i do get local language:

Code: Select all

[MeasureLanguage]
Measure=Registry
RegHKey=HKEY_CURRENT_USER
RegKey=Control Panel\International
RegValue=LocaleName
UpdateDivider=-1

[MeasureTime]
Measure=Time
Format=%#c
FormatLocale=[MeasureLanguage]
DynamicVariables=1
You are right moshi, It was my mistake to assume that the system "locale" would be used by default. In fact, Rainmeter Time measures always assume "English", and you will need to use FormatLocale to effect the output "language / locale".

I have corrected as best I can in the docs and here in the change announcement. Thanks for catching that.
User avatar
moshi
Posts: 1740
Joined: November 13th, 2012, 9:53 pm

Re: New Time and Locale functionality

Post by moshi »

i know you just explained how to deal with it, and i just used the SysInfo plugin in a skin for a few calculations, but TimestampTimeZone (Default: 0) is really missing.

i couldn't get %Z/%z to work with Timestamp/TimestampFormat

as i write, i think i don't really like the idea of TimestampTimeZone.
a new time format for numeric timezone (-0500 for example) would be even better.
User avatar
Brian
Developer
Posts: 2738
Joined: November 24th, 2011, 1:42 am
Location: Utah

Re: New Time and Locale functionality

Post by Brian »

I would also like to note that we (as in Rainmeter code itself) aren't exactly decoding any of the format codes - rather, we are simply passing it on to the system and asking it to figure it all out for us.

That isn't to say we couldn't come up with "something" to address this before we pass it on to Windows, however like jsmorley pointed out, it would be near impossible to get "right" since no one follows any sort of "rules" on the matter. There might be a fix for this (see below).


While testing this new feature I have noticed a few things other than the %z/%Z issue.
#1. Some locales do not use a 12 hour time format, so %p will not read/return anything.
#2. Some locales use a different year system, and the year will be converted to the locales year. For example, try Arabic. FormatLocale=ar The year will be converted to a Hijri year used in the Islamic Calendar.
#3. The date parsed from the TimeStamp will attempt to validate if possible, meaning that certain format codes might be ignored to get the correct date. I have no idea which codes will be ignored (or in which order). I noticed this with an invalid weekday name (%a). For example:

Code: Select all

TimeStamp=Fri, 1 Jul 2014 13:17:26
TimeStampFormat=%a, %d %b %Y %H:%M:%S
Format=%a
This parses the date correctly, however, July 1st 2014 was a Tuesday not a Friday. The output will validate the rest of the date, and then output the correct day of the week (in this case: Tuesday).



It is also worth noting that sometime in the future %z will be changed to support the ISO8601 offset from UTC (ie. +1000). Likewise, new format specifiers: %C, %D, %e, %F, %g, %G, %h, %n, %r, %R, %t, %T, %u, and %V will be supported. E/O modifiers might be available as well. I won't get into what these new specifiers mean at this time since it is unclear "when" these all these options will be available. We will find out when VS2015 comes out.

-Brian
User avatar
moshi
Posts: 1740
Joined: November 13th, 2012, 9:53 pm

Re: New Time and Locale functionality

Post by moshi »

Brian wrote:...
we are simply passing it on to the system and asking it to figure it all out for us.
...
It is also worth noting that sometime in the future %z will be changed to support the ISO8601 offset from UTC (ie. +1000).
sounds great. i am pretty confident that it will be parsed by the system then. this would be exactly what i have been asking for.

Brian wrote:Some locales do not use a 12 hour time format, so %p will not read/return anything.
i noticed that too. that is something to keep in mind for skins using relative (R) Y coordinates.

Brian wrote: The date parsed from the TimeStamp will attempt to validate if possible, meaning that certain format codes might be ignored to get the correct date. I have no idea which codes will be ignored (or in which order). I noticed this with an invalid weekday name
ignoring weekdays makes sense as they are not unique. it would have been nice if a combination of weekday and week number would work, but that does not seem to be the case.