New Release: SRP Utilities 2.1

SRP Utilities 2.1 was released today, providing a handful of new services and routines alongside a couple improvements to existing ones.

 

 

SRP_Num

SRP_Num is a replacement and enhancement to OpenInsight’s built-in Num() function. Using Num() to verify if the expression is a number will not necessarily avoid a runtime error. If the expression is “”, BASIC+ will interpret this as a 0, which might be undesirable. SRP_Num treats “” as a non-numeric value:

A = ""
IsNum = Num(A)     ; // Returns 1
IsNum = SRP_Num(A) ; // Returns 0

Sadly, Num() does not check for unassigned variables and will cause VNAV errors. SRP_Num returns 0 to ensure you won’t get punished when a variable happens to be unassigned:

IsNum = Num(A)     ; // VNAV Error
IsNum = SRP_Num(A) ; // Returns 0

SRP_Num has a second mod that will return any value you want if the variable is not a number. This is useful when you want any non-numerical variable to default to something specific. To use this secondary mode, simply provide a default value in the second parameter. Imagine we are writing a routine that does safe division. We want to make sure both parameters are assigned numbers and that the divisor is never 0, so we can avoid breaking to the debugger with a runtime error.

Here is the typical way we default parameters, but this will break our routine if Divisor is “”, because “” evaluates to 0:

If Unassigned(Dividend) then Dividend = 0
If Unassigned(Divisor) then Divisor = 1

To be safer, we need to check for both “” and unassigned, so you might be tempted to do the following. However, this too will break since BASIC+ evaluates all expressions in a condition, even if the first expression makes the condition true. Thus, the following code will cause a VNAV error either if Dividend or Divisor are unassigned:

If Unassigned(Dividend) OR Dividend EQ "" then Dividend = 0
If Unassigned(Divisor) OR Divisor EQ "" then Divisor = 1

This is the safest way to ensure this routine never breaks to the bugger, but it’s so much code:

If Unassigned(Dividend) then
    Dividend = 0
end else
    If Dividend EQ "" OR Not(Num(Dividend)) then Dividend = 0
end
If Unassigned(Divisor) then
    Divisor = 1
end else
    If Divisor EQ "" OR Not(Num(Divisor)) then Divisor = 1
end

SRP_Num makes it so much simpler:

Dividend = SRP_Num(Dividend, 0)
Divisor = SRP_Num(Divisor, 1)

SRP_Date, SRP_Time, and SRP_DateTime

We deal with dates and times a lot in OpenInsight, and to this day, the primary means by which we manipulate them is with IConv and OConv. These routines, however, come with some limitations. SRP_Date, SRP_Time, and SRP_DateTime provide dozens of services to meet every typical need in a way that is readable and easy to remember.

Extracting Components

You can use SRP_Date, SRP_Time, and SRP_DateTime to extract any component of a date and/or time. Traditionally, if we wanted to extract the month from a date, we’d normally have to use OConv and some string parsing:

Month = OConv(Date(), "D/")[1, "/"]

This isn’t a lot of code, and advanced programmers will figure out what you are doing, but it can be hard to remember which format is best to use, requiring you to look up in the help files how OConv formats dates. If you are using the SRP_Editor, you’ll get a list of services as you type and will easily be able to find the one you are looking for:

Month = SRP_Date("Month", Date())

Conversion

You can easily create and convert between dates, times, and datetimes without having to worry about making mistakes in the arithmetic. In this example, we build a date and time from individual components, then we separate the datetime into a date and a time:

DateTime = SRP_DateTime("Encode", 1941, 12, 8, 7, 51, 43)
Date = SRP_DateTime("Date", DateTime)
Time = SRP_DateTime("Time", DateTime)

Formatting and Parsing

The most powerful features are the Format and Parse services. Never again will you have to remember to lookup the correct mashup of letters to produce the right format:

// produces January 20, 2020
Output = OConv(Date(), "D4,HL")

The Format service uses the same intuitive formatting scheme as our SRP ActiveX Controls and Microsoft Excel. When you want to output the month, you specify M in the format. The more Ms you use, the more “month” you get: M for month number, MM for 2-digit month number, MMM for abbreviated month name, and MMMM for full month name:

// produces January 20, 2020
Output = SRP_Date("Format", Date(), "MMMM D, YYYY")

Likewise, you can parse just about any formatted date and time, using the same formatting syntax. It’s worth noting that our parser is so optimistic it will handle just about anything so long as the date and time components are in the correct order. All of these next examples produce the same valid date, even though the texts are so different and are all using the same format:

Format = "MMMM D, YYYY"
Input = SRP_Date("Parse", "JAN 20, 2020", Format)
Input = SRP_Date("Parse", "1/20/20", Format)
Input = SRP_Date("Parse", "Wednesday, January 20, 2020, the beginning of a new year!", Format)

Cultures

It gets even better for those concerned about culture specific formatting. While IConv and OConv support different languages, they don’t support dynamic formatting. If you want to use the current culture’s formatting, then set the format to “Short” or “Long”. This example produces “1/20/2020” in the US and “20/01/2020” in Spain:

Output = SRP_Date("Format", Date(), "Short")

This one produces “Monday, January 20, 2020” in the US and “lunes, 20 de enero de 2020” in Spain:

Output = SRP_Date("Format", Date(), "Long")

Moreover, you can specify a culture in code, which is useful if you want to manage culture outside of the user’s Windows settings. Here’s how we parse a date using the France’s format and language:

Input = SRP_Date("Parse", "lundi 20 janvier 2020", "Long", "fr-FR")

IConv and OConv

All three services can be used as custom IConv/OConv conversions, so you can use the power of our Format and Parse services in the Form and Table designers. All you have to do is wrap SRP_Date, SRP_Time, or SRP_DateTime in brackets, and follow it with a comma and a format. Here are several examples. Note how we can specify a culture code using parenthesis:

Output = OConv(Datetime(), "[SRP_DATETIME,Short]")
Output = OConv(Datetime(), "[SRP_DATETIME,(es-MX)Long]")
Output = OConv(Datetime(), "[SRP_DATETIME,DDD, MMMM D, YYYY 'at' h:mm:sstt]")
Input = IConv(UserInput, "[SRP_DATETIME,Short]")
Input = IConv(UserInput, "[SRP_DATETIME,MM/DD/YY]")

UTC

Finally, let’s talk a little bit about Coordinated Universal Time, or UTC. UTC is in the GMT time zone, and some applications need time in UTC. You can get the current time in UTC using the following examples. The second parameter is a boolean. When set to 1, the result is in UTC. If omitted or set to 0, you get the current local date and/or time:

CurrentDate = SRP_Date("Today", 1)
CurrentTime = SRP_Time("Now", 1)
CurrentDatetime = SRP_DateTime("Now", 1)

If you already have a date in the database and want to convert to or from UTC, you can do the following.

UTC = SRP_DateTime("ToUTC", MyDate)
Local = SRP_DateTime("FromUTC", UTC)

But wait… there’s more

There are a few more services, like testing if a year is a leap year, but you can review the SRP Utilities documentation to explore those remaining options. Hopefully, this article has already given you more than enough reason to upgrade to 2.1.

Other Tweaks

This release also improves SRP_StopWatch and SRP_String. See the SRP Utilities Version History for details.

Leave a Reply