Legato has several functions to help manage displaying and tracking time. It might be very useful to know, for example, how long has it been since the last action was performed. Say you want to run a task every five minutes. Well, you need some way to check to see if five minutes has elapsed or not. Additionally, if you want to display time to a user, you need to make sure that the time is displayed in an intelligible format, not just a number of milliseconds seconds since 1970 or something equally useless to anyone except a machine. This article will discuss a few of the functions that simplify this.
Friday, January 06. 2017
Legato Developers Corner #16: What Time Is It? Date and Time Functions
Example script of functions discussed in this post:
#define FIVE_DAYS 4320000000000 string datestring; qword filetime; qword futuretime; int unixtime; // We can get the current filetime and print it out filetime = GetLocalTime(); AddMessage("Filetime: %d",filetime); // We can convert it to UNIX time and print it out unixtime = DateToUnix(filetime); AddMessage("Unix Time: %d",unixtime); // We can convert the filetime to a formatted string, and print // out a few examples of different formats datestring = FormatDate(filetime); AddMessage("Default String Format: %s",datestring); datestring = FormatDate(filetime,"l, F dS, Y"); AddMessage("Using a Custom Defined Format: %s",datestring); datestring = FormatDate(filetime, DS_DATE_AT_TIME); AddMessage("Using a Legato Defined Format: %s",datestring); // We can use simple math to get five days from now, and print it // out in a nice date format futuretime = filetime + FIVE_DAYS; datestring = FormatDate(futuretime,"l, F dS, Y"); AddMessage("Five Days From Now: %s",datestring); // StringToDate can try to handle a poorly formatted date and make it // a normalized format in Windows filetime datestring = "2-20/17"; filetime = StringToDate(datestring, SD_AMERICAN); datestring = FormatDate(filetime,"l, F dS, Y"); AddMessage("Reformatted Time: %s",datestring); // When using StringToDate, you may want to test if it was understood or // not. If the GetLastError function is ERROR_SYNTAX, it means the date // was not understood. datestring = "2-20, Two Thousand Seventeen"; filetime = StringToDate(datestring, SD_AMERICAN); if(GetLastError()==ERROR_SYNTAX){ AddMessage("Cannot convert date %s, unknown format",datestring); } // Converting from well-formed MySQL date times is also possible with // StringToDate. datestring = "2017-01-05 12:00:00"; filetime = StringToDate(datestring, SD_AMERICAN); if(GetLastError()==ERROR_SYNTAX){ AddMessage("Cannot convert date, unknown format"); } else{ AddMessage("Converted MySQL Date is: %d",filetime); datestring = FormatDate(filetime,"1, F dS, Y"); AddMessage("Reformatted MySQL Date is: %s", datestring); }
For comparing dates or doing any sort of math with dates, you need to use date-time values. These are stored in Legato as 64-bit qwords, based on the Filetime format described in the Windows SDK. They represent the number of 100 nanosecond intervals since January 1, 1601. With this number, you can store dates as numbers and easily compare them against each other. Legato provides a script function to get this value, called the GetLocalTime function. Once you have this number, you can add a number of 100 nanosecond intervals to it in order to get the time of a future event or subtract time from it. So if you want to get five days from now, it would be the result from the GetLocalTime SDK function plus 4320000000000 100-nanosecond intervals. As we did above, you can store values like that number of intervals using the define statement in your script to make the code more readable.
Comparing dates is easy with this as well. Use normal “less than” (<) and “greater than: (>) operators. Testing for equivalence is a bit trickier because with such a high resolution of the date-time value, you might need to write a function to see if they are equal enough to your particular purpose. If two dates happened within 1 second of each other, for example, is that enough to consider them equal? You could get the difference and compare it against a number of 100-nanosecond intervals, and if the difference is less than that, your function could return true for equivalence.
Another common type of date-time value that you may encounter is POSIX time, also known as Unix time, or Epoch time. This is the number of seconds since January 1, 1970. It is commonly used in a lot of Unix based systems and is very common in web environments. Legato does have functions that will convert Windows Filetime to Unix time and back (the DateToUnix and UnixToDate functions, respectively) in case you need to switch between the two. Make sure whenever you are doing comparisons or date math operations, you know which type of date-time value you are using.
Legato also provides a way for you to convert date strings into Windows Filetime dwords. The SDK function StringToDate takes a date time string and a format value as parameters and returns a Filetime value. It is pretty good at picking up most common types of date time formats (such as MySQL default string formats, a very common date format), but if you specify the format of the incoming data it helps to ensure a proper conversion. The defined values for formats are:
Definition | Bitwise | Description | ||||
Modes | ||||||
SD_PARSEMODE | 0x0000000F | String Parse Mode Mask | ||||
SD_AMERICAN | 0x00000000 | String is American (mm/dd/yy) | ||||
SD_EUROPEAN | 0x00000001 | String is European (dd/mm/yy) | ||||
SD_ISO_8601 | 0x00000002 | String is ISO-8601 | ||||
Options | ||||||
SD_PARTIAL | 0x00010000 | Can be Partially Formatted |
Of course, if you actually want to display time to a user in an intelligible way, you need to convert from Unix time or Windows Filetime formats to a string value. Legato has a very useful function called the FormatDate function that can take either a Windows Filetime date-time qword or an ISO-8601 formatted date string and convert it into whatever date format you want. The date format output can be a normal ISO-8601 style (ISO-8601 is a common way of writing dates, and more information on it can be found here), or it can be anything specified by a format string. The format string can use the following substitution characters (based on PHP 5.x):
Character | Description | Example returned values | ||||
Day | ||||||
d | Day of the month, 2 digits with leading zeros. | 01 to 31 | ||||
D | A textual representation of a day, three letters. | Mon through Sun | ||||
j | Day of the month without leading zeros. | 1 to 31 | ||||
l | A full textual representation of the day of the week. | Sunday through Saturday | ||||
N | ISO-8601 numeric representation of the day of the week. | 1 (for Monday) through 7 (for Sunday) | ||||
S | English ordinal suffix for the day of the month, 2 characters. | st, nd, rd or th. Works well with j | ||||
w | Numeric representation of the day of the week. | 0 (for Sunday) through 6 (for Saturday) | ||||
z | The day of the year (starting from 0) | 0 through 365 | ||||
Week | ||||||
W | ISO-8601 week number of year, weeks starting on Monday (not supported). | Example: 42 (the 42nd week in the year) | ||||
Month | ||||||
F | A full textual representation of a month, such as January or March. | January through December | ||||
m | Numeric representation of a month, with leading zeros. | 01 through 12 | ||||
M | A short textual representation of a month, three letters. | Jan through Dec | ||||
n | Numeric representation of a month, without leading zeros. | 1 through 12 | ||||
t | Number of days in the given month. | 28 through 31 | ||||
Year | ||||||
L | Whether it is a leap year. | 1 if it is a leap year, 0 otherwise. | ||||
o | ISO-8601 year number. This has the same value as Y (PHP supports the exception that if the ISO week number (W) belongs to the previous or next year, that year is used instead. Legato does not.) | Examples: 1999 or 2003 | ||||
Y | A full numeric representation of a year, 4 digits. | Examples: 1999 or 2003 | ||||
y | A two digit representation of a year. | Examples: 99 or 03 | ||||
Time | ||||||
a | Lowercase Ante meridiem and Post meridiem. | am or pm | ||||
A | Uppercase Ante meridiem and Post meridiem. | AM or PM | ||||
B | Swatch Internet time. | 000 through 999 | ||||
g | 12-hour format of an hour without leading zeros. | 1 through 12 | ||||
G | 24-hour format of an hour without leading zeros. | 0 through 23 | ||||
h | 12-hour format of an hour with leading zeros. | 01 through 12 | ||||
H | 24-hour format of an hour with leading zeros. | 00 through 23 | ||||
i | Minutes with leading zeros. | 00 to 59 | ||||
s | Seconds, with leading zeros. | 00 through 59 | ||||
u | Microseconds. | Example: 654321 | ||||
Timezone | ||||||
e | Time zone identifier (not supported). | Examples: UTC, GMT ... | ||||
I | Whether or not the local system is in daylight saving time. | 1 if Daylight Saving Time, 0 otherwise. | ||||
O | Difference to Greenwich time (GMT) in hours. | Example: +0200 | ||||
P | Difference to Greenwich time (GMT) with colon between hours and minutes. | Example: +02:00 | ||||
T | Time zone abbreviation (not supported). | Examples: EST, MDT ... | ||||
Z | Time zone offset in seconds. The offset for time zones west of UTC is always negative, and for those east of UTC is always positive. | -43200 through 50400 | ||||
Full Date/Time | ||||||
c | ISO 8601 date (not supported). | 2004-02-12T15:19:21+00:00 | ||||
r | RFC 2822 formatted date (not supported). | Example: Thu, 21 Dec 2000 16:01:07 +0200 | ||||
U | Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) (not supported). | - |
You can also use one of the predefined formats specified in Legato. Formatting codes can be combined with bitwise operators. Therefore, if you want a mm-dd-yy format but with spaces instead of dashes in the format, you would use the code:
datestring = FormatDate(filetime, DS_DDMMYY | DS_SPACES);
Of course you could replicate this exact style with a custom string format, but this is another way to define the format of the string. The defined formatting codes are:
Definition | Bitwise | Description | Example | ||||
Date Modes | |||||||
DS_MMDDYY | 0x00000000 | mm/dd/yy | 06/30/13 | ||||
DS_MMDDYYYY | 0x00000001 | mm/dd/yyyy | 06/30/2013 | ||||
DS_DDMMYY | 0x00000002 | dd/mm/yy | 30/06/13 | ||||
DS_DDMMYYYY | 0x00000003 | dd/mm/yyyy | 30/06/2013 | ||||
DS_MMMDDYY | 0x00000004 | mmm/dd/yy | JUN 30 13 | ||||
DS_MMMDDYYYY | 0x00000005 | mmm/dd/yyyy | JUN 30 2013 | ||||
DS_DDMMMYY | 0x00000006 | dd/mmm/yy | 30 JUN 13 | ||||
DS_DDMMMYYYY | 0x00000007 | dd/mmm/yyyy | 30 JUN 2013 | ||||
DS_YYYYMMDD | 0x00000008 | yyyy/mm/dd | 2013/06/30 | ||||
DS_MON_DAY_YEAR | 0x00000009 | Mon. Day, Year | Jun. 30, 2013 | ||||
DS_MONTH_DAY_YEAR | 0x0000000A | Month Day, Year | June 30, 2013 | ||||
DS_WEEKDAY_MONTH_DAY_YEAR | 0x0000000B | Weekday, Month Day, Year |
Sunday, June 30, 2013 | ||||
Time Mode | |||||||
DS_HHMM_24 | 0x00000000 | hh:mm (24-hour) | 23:55 | ||||
DS_HHMM_12 | 0x00000010 | hh:mm (12-hour) | 11:55 PM | ||||
DS_HHMMSS_24 | 0x00000020 | hh:mm:ss (24-hour) | 23:55:30 | ||||
DS_HHMMSS_12 | 0x00000030 | hh:mm:ss (12-hour) | 11:55:30 PM | ||||
Date Delimiters | |||||||
DS_SLASH | 0x00000000 | Slashes | 06/30/2013 | ||||
DS_DASH | 0x00010000 | Dashes | 06-30-2013 | ||||
DS_SPACES | 0x00020000 | Spaces | 06 30 2013 | ||||
DS_SPACESCOMMA | 0x00030000 | Spaces and Commas | June 30, 2013 | ||||
DS_PERIODS | 0x00040000 | Periods | 06.30.2013 | ||||
Output Mode | |||||||
DS_DATE | 0x00000000 | Date only | 06/30/2013 | ||||
DS_TIME | 0x01000000 | Time only | 23:55 | ||||
DS_DATE_TIME | 0x02000000 | Date then time | 06/30/2013 23:55 | ||||
DS_DATE_AT_TIME | 0x03000000 | Date at time | 06/30/2013 at 23:55 | ||||
DS_TIME_DATE | 0x04000000 | Time then date | 23:55 06/30/2013 | ||||
DS_DATE_T_TIME | 0x05000000 | ISO-8601 ™ connector | dateTtime | ||||
Case | |||||||
DS_DEFAULT_CASE | 0x00000000 | Default (all lower case) | june 30 2013 | ||||
DS_LOWER | 0x10000000 | Lower | june 30 2013 | ||||
DS_UPPER | 0x20000000 | Upper | JUNE 30 2013 | ||||
DS_INITIAL | 0x30000000 | Initial capitalization | June 30 2013 | ||||
Other | |||||||
DS_ISO_8601 | 0x05010028 | ISO-8601 combination | 2013-06-30T23:55:30 | ||||
DS_NO_ZERO_FILL | 0x80000000 | No zero filling | 6/30/2013 | ||||
DS_FILETIME_UTC | 0x00000100 | Converts as raw UTC | - |
In summary, working with dates and times is commonly a part of scripting, and Legato provides multiple powerful tools to make date-time values easy to create, convert from format to format, and display to users.
Steven Horowitz has been working for Novaworks for over five years as a technical expert with a focus on EDGAR HTML and XBRL. Since the creation of the Legato language in 2015, Steven has been developing scripts to improve the GoFiler user experience. He is currently working toward a Bachelor of Sciences in Software Engineering at RIT and MCC. |
Additional Resources