Recently I was busy developing a Uniface application in a microservice architecture and I was wondering how I could process users and systems from different time zones into one application. As soon as you work with dates and times on the web you will inevitably have to deal with time zones. The Uniface developer cannot avoid this either. After all, more and more applications have a web interface, call webservices or offer a webservice interface themselves.
Uniface offers support for time zones starting from version 9.4. This functionality is disabled by default, but can easily be enabled in the code or in the assignment file. The question is not so much whether Uniface can work with time zones, but how you can do that. I have figured it out for you.
In this article I will explain how you can work with time zones in Uniface. I am also going to explain why it is disabled by default and why you need time zones in the first place. Since I was still working on the time zones, I created a library in which all the time zone functions I discuss in this article are worked out. I will discuss this library in the last paragraph. And the good news is, the library is open source! You can download it for free and use it in your own software.
Why is time zone support disabled by default in Uniface?
Before I explain to you how to use time zones in Uniface, it is important that you know why Uniface has chosen to leave this functionality disabled by default. When an application works with times, it will usually be based on a certain time zone in which it is operating. If all times are within the same time zone, then the concept of time zone is actually not relevant. If you have an existing application, there is a very good chance that time zones are not relevant for your application. If such an application were to work with time zones from one moment to the next, this could have far-reaching consequences.
Doesn’t your organization have interfaces with systems in other time zones? Is the application not developed for use on the internet? Then there is a good chance that you do not have to take different time zones into account at all.
Are time zones relevant, but perhaps you have developed a solution yourself. If an application needs to take time zones into account, but does not use Uniface’s functions for that purpose, it should of course continue to work. By disabling the functionality of time zones by default, it is up to the developer to consciously enable this functionality when needed.
Why do you need time zone support in an application?
In order to know why you would want or need to support time zones in your software, it is important that you know what time zones are. It’s not just an IT question. Every day, consciously or unconsciously, we have to deal with time zones.
Let’s take a look at what time zones are like. We have long agreed that the earth revolves around the sun and its own axis. Considering a circle around the sun as a year and a circle around its own axis is a day. A circle around its own axis is 24 hours. We once decided that it is 12 o’clock when the sun is at its highest point. In this way we have linked time to something mechanical. Not only did time happen instinctively, but it was also visible. Time literally passes across the globe.
A long time ago the world of people was not that big. The distance that could be covered on foot or on horseback was not big enough to realize that it was 12 o’clock elsewhere at another time. The fact that the sun wasn’t at its highest point a hundred kilometers away was really irrelevant, after all, you weren’t there. With the arrival of ships and later especially railroad lines, people physically experienced the inconveniences of the time differences. With the advent of telegraphy and telephony, it also became technically necessary to come up with a solution.
The idea that it is 12 o’clock when the sun is at its highest point would divide the world into an infinite number of time zones. In 1884 it was decided that this was not workable. The world became divided into 24 time zones. Imaginary axes, meridians, running over the earth, between the two poles and thus perpendicular to the equator. The center, the prime meridian, ran across the English Greenwich. From that moment on all times could be represented relative to an average time. That is the Greenwich Meantime, GMT.
Since the GMT itself is also a time zone, it was decided to replace the term GMT with something abstract like the UTC. This seems to be an abbreviation, but it is not. The word UTC is a compromise between the French “TUC” (Temps universel coordonné) and the English “CUT” (Coordinated Universal Time). In IT terms, the military term Zulu time is also often used. UTC time, however, corresponds to GMT time.
Take a look at a world map, with Greenwich in the middle. In all places on the map to the right, so east, of Greenwich it is later. On any point to the left it is earlier. How much later or earlier is shown as an offset to the time in Greenwich. So relative to the UTC time. If somewhere on earth it is 4 hours later than in Greenwich, then this offset to the UTC time is +4:00. Is it 4 hours earlier than the offset -4:00.
In daily life, working with time zones is automatic and instinctive. If you make a reservation in a local restaurant, it is (usually) not necessary to know which time zone you are in. At least, as long as both (you and the restaurant) are in the same time zone… However, do you want to schedule an online Teams meeting with a customer on the other side of the globe? Then it’s nice to know what is meant by half past four in the afternoon. As humans we feel that instinctively.
Applications don’t have instincts, they have to know for sure. If an application only works with times within a certain time zone, it is not necessary to take time zones into account. That is why the time zone functionality of Uniface is also disabled by default. However, as soon as the application needs to work across the time zone boundary, it is relevant to take this into account. After all, half past four in the afternoon in the Netherlands is quite different from half past four in New Zealand or New York. Or is that the same thing? That’s up to the programmer to determine.
How do you work with time zones in Uniface?
Those last sentences of the previous paragraph are important. On the one hand, half past four in the afternoon in the Netherlands is not the same as half past four in another time zone, but on the other hand it is. What do I mean by that? Suppose it is an appointment. In my diary it’s at half past four in the afternoon. I don’t have to take a time zone into account for myself. Only at the moment that I communicate time to someone in a different time zone does the difference in time zones become relevant.
Suppose I make an appointment with someone for a meeting. The meeting is in my time zone at half past four in the afternoon. I put this appointment in my calendar, at half past four. It does not really matter that much to me which time zone someone else is in. As long as we both know that by half past four I mean the half past four in my time zone. Of course annoying for someone on the other side of the world, because that person might have to do something at night. I communicate a certain time and that has to be interpreted by the other in a certain way. We use UTC time for that purpose.
That is also how Uniface looks at time zones. A distinction is made between an internal and an external time. The internal time is, as soon as you start using the Uniface time zone functions, always UTC time. Uniface automatically converts your time to the corresponding UTC time. The external time is the time as shown by the application to the user and is therefore in the local time (zone).
Let me clarify this with an example. I live in the Netherlands and my application runs on a server that is also in the Netherlands. Moreover, if I only have users who live in the Netherlands or in the surrounding countries, then how to do nothing with time zones.
However, as soon as I have a user in Sydney, I might have to take that into account. The matter becomes even more complex when my server is not in the Netherlands, but in India. Moreover, if I use a webservice from a service provider in Los Angeles, I soon have to deal with 4 time zones. To make it even more complex there is also something like daylight saving time and winter time.
In a table it looks as follows, on:
Wednesday November 4, 2020 at 15:30:
|Location||Timezone||UTC offset||Local time|
|Central European Time||+1:00||15:30|
|Australian Eastern Daylight Time||+11:00||5 november 2020 01:30|
|India Standard Time||+5:30||20:00|
|Pacific Standard Time||-8:00||6:30|
In all cases, however, the UTC time is the same: 14:30. So as long as you work with UTC time and indicate the offset, you are always safe!
The Uniface function $nlsinternaltime indicates the internal time zone to use. The function can have two possible values:
- Classic: Uniface does not use the time zone mechanism. This is the default value.
- UTC: Uniface uses UTC time as its internal time zone. With this value you enable the time zone functionality of Uniface..
Note: From the moment $nlsinternaltime gets the value UTC, Uniface will consider all times in an application to be UTC time. This also applies to all times in the database. So if you have an existing application and/or existing data that contains times, those times will be interpreted as being UTC +0:00. Hence again explicitly the warning to be careful with this functionality in an existing application.
By the way, the online documentation of Uniface provides a lot of information about the implementation of time zones in a Uniface application.
Besides the function it is also possible to activate the timezone functionality via the assignment file. Use the setting: $NLS_INTERNAL_TIME. This has the same possible values as the function $nlsinternaltime. As with all assignment settings in Uniface the setting then applies to the whole application. As I will show you later it is wise to use the function so you can control the time zone functionality yourself.
So as soon as you use the time zone functionality, there are always two sides to this matter. The internal time is always UTC. The external time depends on the location of the user.
In Uniface the function $nlstimezone can be used. This has the following possible values:
- Classic: No time zone is defined. Uniface assumes that all time related data is local to the operating system. This is the default.
- System: If the Operating System is Windows, this value can be used to explicitly indicate that the external time must be displayed in the time zone as defined on the system. In many cases the result is the same as for “classic”, but now explicitly.
- Time zone: This is the most interesting value. It allows the system time zone to be overridden. Do you want to format a time for a user or system in a different time zone? Then specify the time zone of the other user.
Uniface also offers an assignment file setting to specify the time zone. This is setting $NLS_TIME_ZONE.
The specified time zone can be every time zone in the world. Uniface accepts all common time zone definitions as defined in the Unicode Common Locale Data Repository (CLDR).
With the help of $nlstimezonelist Uniface can return a list of all supported time zones. Use this function without any parameter will return all, but it can also be used determine the current time zone of the system you are working on. Just use $nlstimezonelist(“system”).
Practical use in Uniface
With only 3 functions ($nlsinternaltime, $nlstimezone and $nlstimezonelist) you have full support of time zones in Uniface. The temptation is great to enable this functionality by default in your application. Of course, there is nothing against that. Nevertheless, I would advise you to consider this carefully. As soon as you enable this functionality by default, Uniface will always process all time related data relative from the UTC time zone. Whether you keep time related data in a database or request the current time with the time functions ($clock, $date and $datim), you will always get UTC time. Even if you perform actions on time-related data, it will always be relative to the UTC time. This also applies to extraction functions in Uniface.
For example. The local time is 16:58 in the Netherlands (wintertime). As soon as I enable the time zone functionality of Uniface, UTC time will be used internally. As a user I see 16:58, in the code this is 15:58. Extracting the number of hours from the time, $clock[H], that is of course 15. The internal time is UTC time.
That is completely according to expectation and also well described in the documentation, but it requires a mindshift from the developer.
As an experienced Uniface developer I want to be in control and I prefer to use Uniface in the way it works with time zones by default. This default is that the time zone functionality is disabled and internal and external time zones are equal (I mean, not relevant). If you need to do something with time zones anyway, it is useful to enable the time zone functionality at that moment, execute the desired functionality and then disable the time zone functions of Uniface again.
By dealing with the time zone functionality in Uniface in this way, this functionality can also be used in existing applications. And then suddenly it turns out to be very powerful!
External time zones are of course only relevant when times are communicated. As I mentioned earlier, making an appointment with someone in another time zone is difficult if you do not indicate which time zone it concerns. After all, half past four in the afternoon in the Netherlands was not always the same as half past four in Sydney.
To avoid confusion you should always indicate the time zone. In the digital world something is only of real value if there is a standard. For communicating times there are many guidelines and many more advices, but the most commonly used is the ISO8601. This displays the date and time including the offset.
For example the time in Amsterdam:
- Localtime: 2020-11-04T15:30:49+01:00
- UTC time: 2020-11-04T14:30:49+00:00
At the same moment in Sydney:
- Localtime: 2020-11-05T01:30:49+11:00
- UTC time: 2020-11-04T14:30:49+00:00
Relative to UTC this is the same time. Therefore the UTC time is the same. However, the local time differs enormously.
With the time zone functions in Uniface this guideline can easily be followed.
All we have to do is determine the UTC offset. With the three time zone related functions this is easy. For example:
variables string vs_tz_datetime datetime vdt_tz, vdt_utc numeric vn_offset endvariables ; first thing: switch on the Uniface time zone support $nlsinternaltime = "UTC" $nlstimezone = "UTC" ; Catch the moment vdt_utc = $datim ; Now switch to the required timezone. Uniface will still process everything in UTC time (due to $nlsinternaltime setting), ; but since the external time (what is shown) is in the required timezone (in $nlstimezone) we need to 'output' the datetime to a string. $nlstimezone = “AEDT” vs_tz_datetime = "%%vdt_utc" ; Now we want to switch to the default / classic mode of Uniface, where all processing is done in the local timezone. ; We are not interested in the timezone anymore. This classic mode just forces Uniface to use the same time for displaying and processing. ; Now assign the ‘captured’ moment to a Uniface datetime variable $nlsinternaltime = "classic" vdt_tz = vs_tz_datetime ; The offset between both is the UTC offset. vn_offset = vdt_tz - vdt_utc
With the result of this routine all date and time calculations can be performed.
Because I needed all kinds of functions to work with time zones anyway, I made a few functions that I would like to share with you.
They are the following functions:
|getISO8601Format||This function returns a date and time in an ISO8601 format. The function can be called with any time zone that Uniface supports. The function can give both local and UTC time.
|GetUTCOffset||This function returns the offset of a certain time zone relative to the UTC time. The basis is that of the example in the previous paragraph. The result is a numerical value that can be used in calculations.
|getUTCOffsetTxt||Nearly the same as GetUTCOffset. Difference is that is returns the offset as a string that can be used to display the offset. It is being used in getISO8601Format for instance.
This library also offers two functions of a supporting nature: leftPad and rightPad to extend a string on the left or right side with a certain character to a certain length.
These functions are published on Gitlab. These functions are published as open source under an Apache 2.0 license. This means that you can use the functions in your own software. The license as well as a manual on how to use it can be found on Gitlab.
These functions should be considered as a first step. Want to improve the features? Or expand the library? If so, participate in the project on Gitlab. How to do that is mentioned in the project.
More about this opensource project can be found in the corresponding post about it: link to this project