Why use ast_localtime() in every place localtime_r() was being used

I see this commit : https://github.com/asterisk/asterisk/commit/5fdba27ea22e1584759dd3c698922d8444c8eb2a ,

use ast_localtime() in every place localtime_r() was being used .

But why ? Any one can tell me ?

The issue is the timezone parse in ast_localtime seems have bug when use musl libc instead glibc .

----log----
root@IAD:~# date 
Thu Jul  4 16:28:01 UTC 2024
root@IAD:~# cat /etc/TZ
UTC-8:00
root@IAD:~# asterisk -rv
Asterisk 16.30.1, Copyright (C) 1999 - 2021, Sangoma Technologies Corporation and others.
Created by Mark Spencer <markster@digium.com>
Asterisk comes with ABSOLUTELY NO WARRANTY; type 'core show warranty' for details.
This is free software, with components licensed under the GNU General Public
License version 2 and other licenses; you are welcome to redistribute it under
certain conditions. Type 'core show license' for details.
=========================================================================
Connected to Asterisk 16.30.1 currently running on IAD (pid = 9199)
[2024-07-04 08:28:14.984 +0000] DEBUG[9257]: res_pjsip_registrar.c:1377 check_expiration_thread: Woke up at 1720081694  Interval: 30
[2024-07-04 08:28:14.984 +0000] DEBUG[9257]: res_pjsip_registrar.c:1384 check_expiration_thread: Expiring 0 contacts
[2024-07-04 08:28:44.985 +0000] DEBUG[9257]: res_pjsip_registrar.c:1377 check_expiration_thread: Woke up at 1720081724  Interval: 30
[2024-07-04 08:28:44.985 +0000] DEBUG[9257]: res_pjsip_registrar.c:1384 check_expiration_thread: Expiring 0 contacts
[2024-07-04 08:29:14.985 +0000] DEBUG[9257]: res_pjsip_registrar.c:1377 check_expiration_thread: Woke up at 1720081754  Interval: 30
[2024-07-04 08:29:14.986 +0000] DEBUG[9257]: res_pjsip_registrar.c:1384 check_expiration_thread: Expiring 0 contacts
IAD*CLI>
------log end--------------------------

as log above , my system time zone is UTC-8:00 , sys localtime is Jul 4 16:28:01 , but the ast locatime is 2024-07-04 08:28 。

It was 17 years ago so I can only guess that to be better portable across different platforms we wrapped the direct calls with those in main/stdtime/localtime.c and replaced the direct calls throughout the code base.

Is there some issue you’re concerned about?

All the standard localtime functions take an implicit timezone setting via the TZ environment variable, whereas ast_localtime takes an explicit timezone specification.

Look in the file main/stdtime/localtime.c in the Asterisk source tree, you’ll see the definition of ast_localtime and a whole lot of code for loading and parsing timezone files, independent of user/system settings.

Yes ,The issue is the timezone parse in ast_localtime seems have bug when use musl libc instead glibc .

----log----
root@IAD:~# date 
Thu Jul  4 16:28:01 UTC 2024
root@IAD:~# cat /etc/TZ
UTC-8:00
root@IAD:~# asterisk -rv
Asterisk 16.30.1, Copyright (C) 1999 - 2021, Sangoma Technologies Corporation and others.
Created by Mark Spencer <markster@digium.com>
Asterisk comes with ABSOLUTELY NO WARRANTY; type 'core show warranty' for details.
This is free software, with components licensed under the GNU General Public
License version 2 and other licenses; you are welcome to redistribute it under
certain conditions. Type 'core show license' for details.
=========================================================================
Connected to Asterisk 16.30.1 currently running on IAD (pid = 9199)
[2024-07-04 08:28:14.984 +0000] DEBUG[9257]: res_pjsip_registrar.c:1377 check_expiration_thread: Woke up at 1720081694  Interval: 30
[2024-07-04 08:28:14.984 +0000] DEBUG[9257]: res_pjsip_registrar.c:1384 check_expiration_thread: Expiring 0 contacts
[2024-07-04 08:28:44.985 +0000] DEBUG[9257]: res_pjsip_registrar.c:1377 check_expiration_thread: Woke up at 1720081724  Interval: 30
[2024-07-04 08:28:44.985 +0000] DEBUG[9257]: res_pjsip_registrar.c:1384 check_expiration_thread: Expiring 0 contacts
[2024-07-04 08:29:14.985 +0000] DEBUG[9257]: res_pjsip_registrar.c:1377 check_expiration_thread: Woke up at 1720081754  Interval: 30
[2024-07-04 08:29:14.986 +0000] DEBUG[9257]: res_pjsip_registrar.c:1384 check_expiration_thread: Expiring 0 contacts
IAD*CLI>
------log end--------------------------

as log above , my system time zone is UTC-8:00 , sys localtime is Jul 4 16:28:01 , but the ast locatime is 2024-07-04 08:28 。

Yes , timezone parse in ast_localtime() seems have bug when using musl libc , I have already added more details, I wan to use localtime_r() back , But I’m worried about side effects .

I’m a bit confused. I know very little about musl but if /etc/TZ shows UTC-08:00 why does running date display the time as UTC? Are you sure that /etc/TZ is even read? From what I just researched about musl and timezones, you have to set the TZ environment variable[1] and I see no mention of /etc/TZ.

  • When you say UTC-08:00 do you mean Pacific Standard Time or Alaska Daylight Time?
  • What’s the output of running echo $TZ from a shell command prompt?
  • What platform are you running on with musl?
  • What is the dateformat parameter set to in the general section of logger.conf?
  • What happens if you set it to dateformat=%F %T.%3q %z?

[1] musl usage guide - Gentoo wiki

  • UTC-08:00 is POSIX format TZ , It means HongKong/BeiJing time , About POSIX TZ
  • echo $TZ return null , and I believe my system do not neet set TZ environment
  • My platform is arm using openwrt , openwrt use musl libc
  • dateformat is already %F %T.%3q %z
root@IAD:~# cat /etc/asterisk/logger.conf 
[general]
dateformat=%F %T.%3q %z

I also tested date "+%F %T %z" cmd , result is

root@IAD:~# cat /etc/TZ 
UTC-8:00
root@IAD:~# date "+%F %T %z"
2024-07-05 10:57:22 +0800

So , May be the real question is asterisk does not compat Posix format TZ ? or May be asterisk never read the /etc/TZ ?

Why not just track down and fix the bug?

Negative offset from UTC means west of UTC, not east.

See TZ-Variable
Actually , The offset specifies the time value you must add to the local time to get a Coordinated Universal Time value. It has syntax like [+ |- ]hh[: mm[: ss]]. This is positive if the local time zone is west of the Prime Meridian and negative if it is east.

Assume there is a bug , I prefer to use localtime_r() , sinece libc can parse tz well .

Yeah, That’s correct, I was wrong, sorry. The sign used when setting the TZ environment variable is the opposite of what’s displayed.

$ TZ=UTC+06:00 date -Is
2024-07-05T10:10:06-06:00

Asterisk does not read /etc/TZ because it’s not a standard location for timezone. It does read /etc/localtime however. If openwrt supplies /usr/share/zoneinfo, then symlink the appropriate file in /usr/share/zoneinfo to /etc/localtime. If openwrt doesn’t provide those files, you may be able to copy the appropriate file from a standard Linux distro to /etc, then symlink localtime to it. For instance…

# On your host:
$ scp /usr/share/zoneinfo/PRC <openwrt>:/etc/

On your openwrt machine:

$ cd /etc
$ ln -s PRC localtime

The file name MUST NOT be changed. If it was PRC on your host, it must be named PRC when copied to /etc on the openwrt machine.

Another alternative would be to edit main/localtime.c and change ast_tzset to always use the TZ environment variable (it only does this for Solaris currently) then rebuild asterisk and set the TZ environment variable in your asterisk startup script.

Thank you !

We would not use /usr/share/zoneinfo due to flash size limitations , ( Openwrt mostly run on a small size flash) .

So I did a patch for main/stdtime/locatime.c , It’s woked for me now . can you help me review it ?

--- a/main/stdtime/localtime.c
+++ b/main/stdtime/localtime.c
@@ -1627,7 +1627,29 @@ static const struct state *ast_tzset(con
        }
 
        if (tzload(zone, sp, TRUE) != 0) {
-               if (zone[0] == ':' || tzparse(zone, sp, FALSE) != 0)
+               /*musl libc use /etc/TZ*/
+               char tzbuff[256];
+               int fd , useTZ = 0;
+
+               memset(tzbuff, 0, sizeof(tzbuff));
+               if ((fd = open("/etc/TZ", OPEN_MODE)) > 0) {
+                       int n = read(fd, tzbuff, sizeof(tzbuff));
+                       if(n > 0) {
+                               /*remove \r \n*/
+                               char *p = tzbuff;
+                               while(*p != 0) {
+                                       if(*p == '\r' || *p == '\n')
+                                               *p = 0;
+                                       p++;
+                               }
+                               if(tzparse(tzbuff, sp, FALSE) == 0) {
+                                       useTZ = 1;
+                               }
+                       }
+                       close(fd);
+               }
+
+               if (!useTZ && (zone[0] == ':' || tzparse(zone, sp, FALSE) != 0))
                        (void) gmtload(sp);
        }
        ast_copy_string(sp->name, zone, sizeof(sp->name));

This isn’t the place to get code reviewed. If you actually want it included you would need to submit a Github pull request after signing the contributor license agreement.

Okay, I think my issue is solved , I will close this case , Thank you !