It seems that that the DATETIME-TZ function can figure out the DST timezone if
Now using the below example code we can see that the DATETIME-TZ function figures out the correct timezone of each date, accurate to their DST timezone.
def var ldtWinterTS as datetime-tz. def var ldtSummerTS as datetime-tz. ldtWinterTS = DATETIME-TZ(02, 24, 2019, 0, 0, 0). ldtSummerTS = DATETIME-TZ(06, 19, 2019, 0, 0, 0). DISP ldtWinterTS LABEL "Winter TS" SKIP ldtSummerTS LABEL "Summer TS" SKIP WITH SIDE-LABEL.
RESULT:
Winter TS: 24/02/2019 00:00:00.000+01:00 Summer TS: 19/06/2019 00:00:00.000+02:00
I would like to know
Thanks [mention:679595a048594a6a8dc3cbd6c5c182f2:e9ed411860ed4f2ba0265705b8793d05] for your reply. I ended up doing something similar. It's direct call to OS in order to use the tzdb (Olson db). The usage of this in our case not extensive (and only limited to unix/linux os) so it is not much of an overkill for us. This method can be used to convert any timezone timestamp with correct timezone offset, even when the stored UTC timestamp was in a different timezone due to DST rules.
METHOD PUBLIC STATIC DATETIME-TZ ConvertIsoToLocal(icISOTimeStamp AS CHAR, icTZCanonicalId AS CHAR): /* Convert ISO format (YYYY-MM-DDTHH:MM:SS+Z) timestamps to desired timezone's timestamp. input tz should be a valid canonical id */ DEF VAR lcDateFormat AS CHAR NO-UNDO. DEF VAR lcResult AS CHAR NO-UNDO. DEF VAR lcCommand AS CHAR NO-UNDO. DEF VAR lcArguments AS CHAR NO-UNDO. DEF VAR lcTSFormat AS CHAR NO-UNDO. lcDateFormat = SESSION:DATE-FORMAT. SESSION:DATE-FORMAT = "ymd". /* if OS is not unix/linux then this method cannot evaluate. hence return original timestamp */ IF OPSYS <> "UNIX" THEN RETURN DATETIME-TZ(icISOTimeStamp). /* prepare the command and format of timestamp */ lcTSFormat = "+'%Y-%m-%d %H:%M:%S.%3N%:z'". lcCommand = SUBST("TZ='&1' date", icTZCanonicalId). lcArguments = SUBST("-d '&1' &2", icISOTimeStamp, lcTSFormat). /* call the os date function and capture the resulting timestamp */ INPUT THROUGH VALUE(lcCommand) VALUE(lcArguments) NO-ECHO. IMPORT UNFORMATTED lcResult. INPUT CLOSE. RETURN DATETIME-TZ(lcResult). FINALLY: SESSION:DATE-FORMAT = lcDateFormat. END FINALLY. END METHOD.
Thanks [mention:9e4ee96fac634b8f91b580e1fb4f7e71:e9ed411860ed4f2ba0265705b8793d05] for the answer. Yes I understand it will use the SESSION:TIME-SOURCE as a source but what I am after is:
1. how exactly the DATETIME-TZ function figures out the DST timezones? Is this implemented and written in Progress ABL or does it use some OS library underneath? Any pointer to implementation/code would be nice to have.
2. can this feature be reused somehow in OE application/implementations?
If there is a call to OS function (or similar functionality) then it would be nice to know more about the implementation or actual code if possible somehow. It might help resolve a lot of datetime-tz related issue where DST needs to be considered for UI displays while timestamps are stored in UTC format. Any info would be really helpful.
If you're using Windows, the following .Net Progress code might give you some ideas.
IF System.TimeZone:CurrentTimeZone:IsDaylightSavingTime(System.DateTime:Now)
THEN MESSAGE System.TimeZone:CurrentTimeZone:DaylightName
VIEW-AS ALERT-BOX.
ELSE MESSAGE System.TimeZone:CurrentTimeZone:StandardName
VIEW-AS ALERT-BOX.
Thanks [mention:3bdfc97448124857a5b85c01a3d4da63:e9ed411860ed4f2ba0265705b8793d05]. I'm looking for something that works at least on Linux/Unix environment.
I'm curious about the precise use-case. You simply said: "... resolve a lot of datetime-tz related issue where DST needs to be considered for UI displays ...".
Are you saying that you want to be able to visually indicate to the user whether the related point of time was daylight savings or not? This is a bit confusing to me. Normally it is up to the user to pick their "preference" of timezone, usually in the hosting OS. Once they've selected it, the user should know for themselves what the rules are for their own timezone.
For example, if I live in EST5EDT and I have a very short business trip to another timezone that is three hours away (eg. PST8PDT) then I might continue keeping displaying my times in EST5EDT if I desire ... it is my own choice ... nothing forces me to change the visual representations of my date/times (eg in my audit logs or whatever).
I suspect that the best information you can get out of OE is the offset from UTC, as you are already aware (+01:00 or +02:00)
RESULT:
Winter TS: 24/02/2019 00:00:00.000+01:00
Summer TS: 19/06/2019 00:00:00.000+02:00
... the problem is that timezone stuff works very differently from one OS to another. If you want to do better than displaying "+01:00" then you probably just shell out to the OS:
(host:/home/me)date Thu Sep 5 09:03:32 EDT 2019
Personally I think it might be overkill, if your users already have a way to select their preference of timezone in the OS - assuming they understand how that selected timezone behaves.
When the DATETIME-TZ function is not supplied a timezone, we calculate the timezone using the c-runtime localtime function:
struct tm *localtime(const time_t *timer)
The tm structure has a member named tm_isdst, indicating whether the date/time value is in daylight savings time. The OS provides this information.
Thanks [mention:679595a048594a6a8dc3cbd6c5c182f2:e9ed411860ed4f2ba0265705b8793d05] for your reply. I ended up doing something similar. It's direct call to OS in order to use the tzdb (Olson db). The usage of this in our case not extensive (and only limited to unix/linux os) so it is not much of an overkill for us. This method can be used to convert any timezone timestamp with correct timezone offset, even when the stored UTC timestamp was in a different timezone due to DST rules.
METHOD PUBLIC STATIC DATETIME-TZ ConvertIsoToLocal(icISOTimeStamp AS CHAR, icTZCanonicalId AS CHAR): /* Convert ISO format (YYYY-MM-DDTHH:MM:SS+Z) timestamps to desired timezone's timestamp. input tz should be a valid canonical id */ DEF VAR lcDateFormat AS CHAR NO-UNDO. DEF VAR lcResult AS CHAR NO-UNDO. DEF VAR lcCommand AS CHAR NO-UNDO. DEF VAR lcArguments AS CHAR NO-UNDO. DEF VAR lcTSFormat AS CHAR NO-UNDO. lcDateFormat = SESSION:DATE-FORMAT. SESSION:DATE-FORMAT = "ymd". /* if OS is not unix/linux then this method cannot evaluate. hence return original timestamp */ IF OPSYS <> "UNIX" THEN RETURN DATETIME-TZ(icISOTimeStamp). /* prepare the command and format of timestamp */ lcTSFormat = "+'%Y-%m-%d %H:%M:%S.%3N%:z'". lcCommand = SUBST("TZ='&1' date", icTZCanonicalId). lcArguments = SUBST("-d '&1' &2", icISOTimeStamp, lcTSFormat). /* call the os date function and capture the resulting timestamp */ INPUT THROUGH VALUE(lcCommand) VALUE(lcArguments) NO-ECHO. IMPORT UNFORMATTED lcResult. INPUT CLOSE. RETURN DATETIME-TZ(lcResult). FINALLY: SESSION:DATE-FORMAT = lcDateFormat. END FINALLY. END METHOD.