]> git.pld-linux.org Git - packages/php.git/blame - php-systzdata.patch
up to php-7.2.0RC6, likely last RC
[packages/php.git] / php-systzdata.patch
CommitLineData
cacce04b
ER
1# License: MIT
2# http://opensource.org/licenses/MIT
3
c0240cb1 4Add support for use of the system timezone database, rather
5than embedding a copy. Discussed upstream but was not desired.
6
98ff8200
ER
7(Few) upstream reports:
8https://bugs.php.net/bug.php?id=53320
9https://bugs.php.net/bug.php?id=54250
10
c0240cb1 11History:
cacce04b
ER
12r15: adapt for timelib 2017.05beta7 (in 7.2.0RC1)
13r14: improve check for valid tz file
909e9211 14r13: adapt for upstream changes to use PHP allocator
a659fa18
ER
15r12: adapt for upstream changes for new zic
16r11: use canonical names to avoid more case sensitivity issues
17 round lat/long from zone.tab towards zero per builtin db
18r10: make timezone case insensitive
361fb152
ER
19r9: fix another compile error without --with-system-tzdata configured (Michael Heimpold)
20r8: fix compile error without --with-system-tzdata configured
c0240cb1 21r7: improve check for valid timezone id to exclude directories
a659fa18 22r6: fix fd leak in r5, fix country code/BC flag use in
c0240cb1 23 timezone_identifiers_list() using system db,
24 fix use of PECL timezonedb to override system db,
25r5: reverts addition of "System/Localtime" fake tzname.
26 updated for 5.3.0, parses zone.tab to pick up mapping between
27 timezone name, country code and long/lat coords
28r4: added "System/Localtime" tzname which uses /etc/localtime
29r3: fix a crash if /usr/share/zoneinfo doesn't exist (Raphael Geissert)
30r2: add filesystem trawl to set up name alias index
31r1: initial revision
32
cacce04b
ER
33diff -up ./ext/date/lib/parse_tz.c.systzdata ./ext/date/lib/parse_tz.c
34--- ./ext/date/lib/parse_tz.c.systzdata 2017-08-22 09:40:38.000000000 +0200
35+++ ./ext/date/lib/parse_tz.c 2017-08-22 12:16:00.370298079 +0200
36@@ -25,8 +25,21 @@
c0240cb1 37 #include "timelib.h"
cacce04b 38 #include "timelib_private.h"
c0240cb1 39
40+#ifdef HAVE_SYSTEM_TZDATA
41+#include <sys/mman.h>
42+#include <sys/stat.h>
43+#include <limits.h>
44+#include <fcntl.h>
45+#include <unistd.h>
46+
47+#include "php_scandir.h"
c0240cb1 48+
cacce04b 49+#else
909e9211 50 #define TIMELIB_SUPPORTS_V2DATA
c0240cb1 51 #include "timezonedb.h"
52+#endif
53+
54+#include <ctype.h>
55
56 #if (defined(__APPLE__) || defined(__APPLE_CC__)) && (defined(__BIG_ENDIAN__) || defined(__LITTLE_ENDIAN__))
57 # if defined(__LITTLE_ENDIAN__)
cacce04b 58@@ -67,6 +80,11 @@ static int read_php_preamble(const unsig
c0240cb1 59 {
a659fa18
ER
60 uint32_t version;
61
62+ if (memcmp(*tzf, "TZif", 4) == 0) {
63+ *tzf += 20;
64+ return 0;
65+ }
909e9211 66+
a659fa18
ER
67 /* read ID */
68 version = (*tzf)[3] - '0';
69 *tzf += 4;
cacce04b 70@@ -374,7 +392,429 @@ void timelib_dump_tzinfo(timelib_tzinfo
c0240cb1 71 }
72 }
73
74-static int seek_to_tz_position(const unsigned char **tzf, char *timezone, const timelib_tzdb *tzdb)
75+#ifdef HAVE_SYSTEM_TZDATA
76+
77+#ifdef HAVE_SYSTEM_TZDATA_PREFIX
78+#define ZONEINFO_PREFIX HAVE_SYSTEM_TZDATA_PREFIX
79+#else
80+#define ZONEINFO_PREFIX "/usr/share/zoneinfo"
81+#endif
82+
83+/* System timezone database pointer. */
7588b36c 84+static const timelib_tzdb *timezonedb_system;
c0240cb1 85+
86+/* Hash table entry for the cache of the zone.tab mapping table. */
87+struct location_info {
88+ char code[2];
89+ double latitude, longitude;
90+ char name[64];
91+ char *comment;
92+ struct location_info *next;
93+};
94+
95+/* Cache of zone.tab. */
96+static struct location_info **system_location_table;
97+
98+/* Size of the zone.tab hash table; a random-ish prime big enough to
99+ * prevent too many collisions. */
100+#define LOCINFO_HASH_SIZE (1021)
101+
7588b36c 102+/* Compute a case insensitive hash of str */
c0240cb1 103+static uint32_t tz_hash(const char *str)
104+{
105+ const unsigned char *p = (const unsigned char *)str;
106+ uint32_t hash = 5381;
107+ int c;
909e9211 108+
7588b36c 109+ while ((c = tolower(*p++)) != '\0') {
c0240cb1 110+ hash = (hash << 5) ^ hash ^ c;
111+ }
909e9211 112+
c0240cb1 113+ return hash % LOCINFO_HASH_SIZE;
114+}
115+
116+/* Parse an ISO-6709 date as used in zone.tab. Returns end of the
117+ * parsed string on success, or NULL on parse error. On success,
118+ * writes the parsed number to *result. */
119+static char *parse_iso6709(char *p, double *result)
120+{
121+ double v, sign;
122+ char *pend;
123+ size_t len;
124+
125+ if (*p == '+')
126+ sign = 1.0;
127+ else if (*p == '-')
128+ sign = -1.0;
129+ else
130+ return NULL;
131+
132+ p++;
133+ for (pend = p; *pend >= '0' && *pend <= '9'; pend++)
134+ ;;
135+
136+ /* Annoying encoding used by zone.tab has no decimal point, so use
137+ * the length to determine the format:
138+ *
139+ * 4 = DDMM
140+ * 5 = DDDMM
141+ * 6 = DDMMSS
142+ * 7 = DDDMMSS
143+ */
144+ len = pend - p;
145+ if (len < 4 || len > 7) {
146+ return NULL;
147+ }
148+
149+ /* p => [D]DD */
150+ v = (p[0] - '0') * 10.0 + (p[1] - '0');
151+ p += 2;
152+ if (len == 5 || len == 7)
153+ v = v * 10.0 + (*p++ - '0');
154+ /* p => MM[SS] */
155+ v += (10.0 * (p[0] - '0')
156+ + p[1] - '0') / 60.0;
157+ p += 2;
158+ /* p => [SS] */
159+ if (len > 5) {
160+ v += (10.0 * (p[0] - '0')
161+ + p[1] - '0') / 3600.0;
162+ p += 2;
163+ }
164+
165+ /* Round to five decimal place, not because it's a good idea,
166+ * but, because the builtin data uses rounded data, so, match
167+ * that. */
a659fa18 168+ *result = trunc(v * sign * 100000.0) / 100000.0;
c0240cb1 169+
170+ return p;
171+}
172+
173+/* This function parses the zone.tab file to build up the mapping of
174+ * timezone to country code and geographic location, and returns a
175+ * hash table. The hash table is indexed by the function:
176+ *
177+ * tz_hash(timezone-name)
178+ */
179+static struct location_info **create_location_table(void)
180+{
181+ struct location_info **li, *i;
182+ char zone_tab[PATH_MAX];
183+ char line[512];
184+ FILE *fp;
185+
186+ strncpy(zone_tab, ZONEINFO_PREFIX "/zone.tab", sizeof zone_tab);
187+
188+ fp = fopen(zone_tab, "r");
189+ if (!fp) {
190+ return NULL;
191+ }
192+
193+ li = calloc(LOCINFO_HASH_SIZE, sizeof *li);
194+
195+ while (fgets(line, sizeof line, fp)) {
196+ char *p = line, *code, *name, *comment;
197+ uint32_t hash;
198+ double latitude, longitude;
199+
200+ while (isspace(*p))
201+ p++;
202+
203+ if (*p == '#' || *p == '\0' || *p == '\n')
204+ continue;
205+
206+ if (!isalpha(p[0]) || !isalpha(p[1]) || p[2] != '\t')
207+ continue;
208+
209+ /* code => AA */
210+ code = p;
211+ p[2] = 0;
212+ p += 3;
213+
214+ /* coords => [+-][D]DDMM[SS][+-][D]DDMM[SS] */
215+ p = parse_iso6709(p, &latitude);
216+ if (!p) {
217+ continue;
218+ }
219+ p = parse_iso6709(p, &longitude);
220+ if (!p) {
221+ continue;
222+ }
223+
224+ if (!p || *p != '\t') {
225+ continue;
226+ }
227+
228+ /* name = string */
229+ name = ++p;
230+ while (*p != '\t' && *p && *p != '\n')
231+ p++;
232+
233+ *p++ = '\0';
234+
235+ /* comment = string */
236+ comment = p;
237+ while (*p != '\t' && *p && *p != '\n')
238+ p++;
239+
240+ if (*p == '\n' || *p == '\t')
241+ *p = '\0';
242+
243+ hash = tz_hash(name);
244+ i = malloc(sizeof *i);
245+ memcpy(i->code, code, 2);
246+ strncpy(i->name, name, sizeof i->name);
247+ i->comment = strdup(comment);
248+ i->longitude = longitude;
249+ i->latitude = latitude;
250+ i->next = li[hash];
251+ li[hash] = i;
252+ /* printf("%s [%u, %f, %f]\n", name, hash, latitude, longitude); */
253+ }
254+
255+ fclose(fp);
256+
257+ return li;
258+}
259+
260+/* Return location info from hash table, using given timezone name.
261+ * Returns NULL if the name could not be found. */
262+const struct location_info *find_zone_info(struct location_info **li,
263+ const char *name)
264+{
265+ uint32_t hash = tz_hash(name);
266+ const struct location_info *l;
267+
268+ if (!li) {
269+ return NULL;
270+ }
271+
272+ for (l = li[hash]; l; l = l->next) {
273+ if (strcasecmp(l->name, name) == 0)
274+ return l;
275+ }
276+
277+ return NULL;
278+}
279+
280+/* Filter out some non-tzdata files and the posix/right databases, if
281+ * present. */
282+static int index_filter(const struct dirent *ent)
283+{
284+ return strcmp(ent->d_name, ".") != 0
285+ && strcmp(ent->d_name, "..") != 0
286+ && strcmp(ent->d_name, "posix") != 0
287+ && strcmp(ent->d_name, "posixrules") != 0
288+ && strcmp(ent->d_name, "right") != 0
cacce04b 289+ && strstr(ent->d_name, ".list") == NULL
c0240cb1 290+ && strstr(ent->d_name, ".tab") == NULL;
291+}
292+
293+static int sysdbcmp(const void *first, const void *second)
294+{
295+ const timelib_tzdb_index_entry *alpha = first, *beta = second;
296+
a659fa18 297+ return strcasecmp(alpha->id, beta->id);
c0240cb1 298+}
299+
300+
301+/* Create the zone identifier index by trawling the filesystem. */
302+static void create_zone_index(timelib_tzdb *db)
303+{
304+ size_t dirstack_size, dirstack_top;
305+ size_t index_size, index_next;
306+ timelib_tzdb_index_entry *db_index;
307+ char **dirstack;
308+
309+ /* LIFO stack to hold directory entries to scan; each slot is a
310+ * directory name relative to the zoneinfo prefix. */
311+ dirstack_size = 32;
312+ dirstack = malloc(dirstack_size * sizeof *dirstack);
313+ dirstack_top = 1;
314+ dirstack[0] = strdup("");
315+
316+ /* Index array. */
317+ index_size = 64;
318+ db_index = malloc(index_size * sizeof *db_index);
319+ index_next = 0;
320+
321+ do {
322+ struct dirent **ents;
323+ char name[PATH_MAX], *top;
324+ int count;
325+
326+ /* Pop the top stack entry, and iterate through its contents. */
327+ top = dirstack[--dirstack_top];
328+ snprintf(name, sizeof name, ZONEINFO_PREFIX "/%s", top);
329+
330+ count = php_scandir(name, &ents, index_filter, php_alphasort);
331+
332+ while (count > 0) {
333+ struct stat st;
334+ const char *leaf = ents[count - 1]->d_name;
335+
336+ snprintf(name, sizeof name, ZONEINFO_PREFIX "/%s/%s",
337+ top, leaf);
338+
339+ if (strlen(name) && stat(name, &st) == 0) {
340+ /* Name, relative to the zoneinfo prefix. */
341+ const char *root = top;
342+
343+ if (root[0] == '/') root++;
344+
345+ snprintf(name, sizeof name, "%s%s%s", root,
346+ *root ? "/": "", leaf);
347+
348+ if (S_ISDIR(st.st_mode)) {
349+ if (dirstack_top == dirstack_size) {
350+ dirstack_size *= 2;
351+ dirstack = realloc(dirstack,
352+ dirstack_size * sizeof *dirstack);
353+ }
354+ dirstack[dirstack_top++] = strdup(name);
355+ }
356+ else {
357+ if (index_next == index_size) {
358+ index_size *= 2;
359+ db_index = realloc(db_index,
360+ index_size * sizeof *db_index);
361+ }
362+
363+ db_index[index_next++].id = strdup(name);
364+ }
365+ }
366+
367+ free(ents[--count]);
368+ }
369+
370+ if (count != -1) free(ents);
371+ free(top);
372+ } while (dirstack_top);
373+
374+ qsort(db_index, index_next, sizeof *db_index, sysdbcmp);
375+
376+ db->index = db_index;
377+ db->index_size = index_next;
378+
379+ free(dirstack);
380+}
381+
382+#define FAKE_HEADER "1234\0??\1??"
383+#define FAKE_UTC_POS (7 - 4)
384+
385+/* Create a fake data segment for database 'sysdb'. */
386+static void fake_data_segment(timelib_tzdb *sysdb,
387+ struct location_info **info)
388+{
389+ size_t n;
390+ char *data, *p;
391+
392+ data = malloc(3 * sysdb->index_size + 7);
393+
394+ p = mempcpy(data, FAKE_HEADER, sizeof(FAKE_HEADER) - 1);
395+
396+ for (n = 0; n < sysdb->index_size; n++) {
397+ const struct location_info *li;
398+ timelib_tzdb_index_entry *ent;
399+
400+ ent = (timelib_tzdb_index_entry *)&sysdb->index[n];
401+
402+ /* Lookup the timezone name in the hash table. */
403+ if (strcmp(ent->id, "UTC") == 0) {
404+ ent->pos = FAKE_UTC_POS;
405+ continue;
406+ }
407+
408+ li = find_zone_info(info, ent->id);
409+ if (li) {
410+ /* If found, append the BC byte and the
411+ * country code; set the position for this
412+ * section of timezone data. */
413+ ent->pos = (p - data) - 4;
414+ *p++ = '\1';
415+ *p++ = li->code[0];
416+ *p++ = li->code[1];
417+ }
418+ else {
419+ /* If not found, the timezone data can
420+ * point at the header. */
421+ ent->pos = 0;
422+ }
423+ }
424+
425+ sysdb->data = (unsigned char *)data;
426+}
427+
428+/* Returns true if the passed-in stat structure describes a
429+ * probably-valid timezone file. */
cacce04b 430+static int is_valid_tzfile(const struct stat *st, int fd)
c0240cb1 431+{
cacce04b
ER
432+ if (fd) {
433+ char buf[20];
434+ if (read(fd, buf, 20)!=20) {
435+ return 0;
436+ }
437+ lseek(fd, SEEK_SET, 0);
438+ if (memcmp(buf, "TZif", 4)) {
439+ return 0;
440+ }
441+ }
c0240cb1 442+ return S_ISREG(st->st_mode) && st->st_size > 20;
443+}
444+
a659fa18
ER
445+/* To allow timezone names to be used case-insensitively, find the
446+ * canonical name for this timezone, if possible. */
447+static const char *canonical_tzname(const char *timezone)
448+{
449+ if (timezonedb_system) {
450+ timelib_tzdb_index_entry *ent, lookup;
909e9211 451+
a659fa18 452+ lookup.id = (char *)timezone;
909e9211 453+
a659fa18
ER
454+ ent = bsearch(&lookup, timezonedb_system->index,
455+ timezonedb_system->index_size, sizeof lookup,
456+ sysdbcmp);
457+ if (ent) {
458+ return ent->id;
459+ }
460+ }
461+
462+ return timezone;
463+}
464+
c0240cb1 465+/* Return the mmap()ed tzfile if found, else NULL. On success, the
466+ * length of the mapped data is placed in *length. */
467+static char *map_tzfile(const char *timezone, size_t *length)
468+{
469+ char fname[PATH_MAX];
470+ struct stat st;
471+ char *p;
472+ int fd;
473+
474+ if (timezone[0] == '\0' || strstr(timezone, "..") != NULL) {
475+ return NULL;
476+ }
477+
a659fa18 478+ snprintf(fname, sizeof fname, ZONEINFO_PREFIX "/%s", canonical_tzname(timezone));
cacce04b 479+
c0240cb1 480+ fd = open(fname, O_RDONLY);
481+ if (fd == -1) {
482+ return NULL;
cacce04b 483+ } else if (fstat(fd, &st) != 0 || !is_valid_tzfile(&st, fd)) {
c0240cb1 484+ close(fd);
485+ return NULL;
486+ }
487+
488+ *length = st.st_size;
489+ p = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
490+ close(fd);
491+
492+ return p != MAP_FAILED ? p : NULL;
493+}
494+
495+#endif
496+
497+static int inmem_seek_to_tz_position(const unsigned char **tzf, char *timezone, const timelib_tzdb *tzdb)
498 {
499 int left = 0, right = tzdb->index_size - 1;
500 #ifdef HAVE_SETLOCALE
cacce04b 501@@ -419,9 +859,48 @@ static int seek_to_tz_position(const uns
c0240cb1 502 return 0;
503 }
504
a659fa18 505+static int seek_to_tz_position(const unsigned char **tzf, char *timezone,
c0240cb1 506+ char **map, size_t *maplen,
507+ const timelib_tzdb *tzdb)
508+{
361fb152 509+#ifdef HAVE_SYSTEM_TZDATA
c0240cb1 510+ if (tzdb == timezonedb_system) {
511+ char *orig;
512+
513+ orig = map_tzfile(timezone, maplen);
514+ if (orig == NULL) {
515+ return 0;
516+ }
a659fa18 517+
909e9211 518+ (*tzf) = (unsigned char *)orig;
c0240cb1 519+ *map = orig;
909e9211 520+ return 1;
c0240cb1 521+ }
a659fa18 522+ else
361fb152 523+#endif
a659fa18 524+ {
c0240cb1 525+ return inmem_seek_to_tz_position(tzf, timezone, tzdb);
526+ }
527+}
528+
529 const timelib_tzdb *timelib_builtin_db(void)
530 {
531+#ifdef HAVE_SYSTEM_TZDATA
532+ if (timezonedb_system == NULL) {
533+ timelib_tzdb *tmp = malloc(sizeof *tmp);
534+
535+ tmp->version = "0.system";
536+ tmp->data = NULL;
537+ create_zone_index(tmp);
538+ system_location_table = create_location_table();
a659fa18 539+ fake_data_segment(tmp, system_location_table);
c0240cb1 540+ timezonedb_system = tmp;
541+ }
542+
c0240cb1 543+ return timezonedb_system;
544+#else
545 return &timezonedb_builtin;
546+#endif
547 }
548
cacce04b
ER
549 const timelib_tzdb_index_entry *timelib_timezone_identifiers_list(timelib_tzdb *tzdb, int *count)
550@@ -433,7 +912,30 @@ const timelib_tzdb_index_entry *timelib_
c0240cb1 551 int timelib_timezone_id_is_valid(char *timezone, const timelib_tzdb *tzdb)
552 {
553 const unsigned char *tzf;
554- return (seek_to_tz_position(&tzf, timezone, tzdb));
555+
556+#ifdef HAVE_SYSTEM_TZDATA
a659fa18
ER
557+ if (tzdb == timezonedb_system) {
558+ char fname[PATH_MAX];
559+ struct stat st;
560+
561+ if (timezone[0] == '\0' || strstr(timezone, "..") != NULL) {
562+ return 0;
563+ }
c0240cb1 564+
a659fa18
ER
565+ if (system_location_table) {
566+ if (find_zone_info(system_location_table, timezone) != NULL) {
567+ /* found in cache */
568+ return 1;
569+ }
570+ }
571+
572+ snprintf(fname, sizeof fname, ZONEINFO_PREFIX "/%s", canonical_tzname(timezone));
573+
cacce04b 574+ return stat(fname, &st) == 0 && is_valid_tzfile(&st, 0);
a659fa18
ER
575+ }
576+#endif
909e9211 577+
c0240cb1 578+ return (inmem_seek_to_tz_position(&tzf, timezone, tzdb));
579 }
580
cacce04b
ER
581 static int skip_64bit_preamble(const unsigned char **tzf, timelib_tzinfo *tz)
582@@ -475,12 +977,14 @@ static timelib_tzinfo* timelib_tzinfo_ct
583 timelib_tzinfo *timelib_parse_tzfile(char *timezone, const timelib_tzdb *tzdb, int *error_code)
c0240cb1 584 {
585 const unsigned char *tzf;
586+ char *memmap = NULL;
587+ size_t maplen;
588 timelib_tzinfo *tmp;
a659fa18 589 int version;
cacce04b
ER
590 int transitions_result, types_result;
591 unsigned int type; /* TIMELIB_TZINFO_PHP or TIMELIB_TZINFO_ZONEINFO */
c0240cb1 592
593- if (seek_to_tz_position(&tzf, timezone, tzdb)) {
594+ if (seek_to_tz_position(&tzf, timezone, &memmap, &maplen, tzdb)) {
595 tmp = timelib_tzinfo_ctor(timezone);
596
cacce04b
ER
597 version = read_preamble(&tzf, tmp, &type);
598@@ -503,6 +1007,29 @@ timelib_tzinfo *timelib_parse_tzfile(cha
599 timelib_tzinfo_dtor(tmp);
600 return NULL;
601 }
c0240cb1 602+
603+#ifdef HAVE_SYSTEM_TZDATA
604+ if (memmap) {
605+ const struct location_info *li;
606+
607+ /* TZif-style - grok the location info from the system database,
608+ * if possible. */
609+
610+ if ((li = find_zone_info(system_location_table, timezone)) != NULL) {
909e9211
ER
611+ tmp->location.comments = timelib_strdup(li->comment);
612+ strncpy(tmp->location.country_code, li->code, 2);
c0240cb1 613+ tmp->location.longitude = li->longitude;
614+ tmp->location.latitude = li->latitude;
615+ tmp->bc = 1;
616+ }
617+ else {
cacce04b 618+ set_default_location_and_comments(&tzf, tmp);
c0240cb1 619+ }
620+
621+ /* Now done with the mmap segment - discard it. */
622+ munmap(memmap, maplen);
cacce04b
ER
623+ } else {
624+#endif
625 if (version == 2 || version == 3) {
626 if (!skip_64bit_preamble(&tzf, tmp)) {
627 /* 64 bit preamble is not in place */
628@@ -520,6 +1047,9 @@ timelib_tzinfo *timelib_parse_tzfile(cha
629 } else {
630 set_default_location_and_comments(&tzf, tmp);
631 }
632+#ifdef HAVE_SYSTEM_TZDATA
633+ }
c0240cb1 634+#endif
c0240cb1 635 } else {
cacce04b 636 *error_code = TIMELIB_ERROR_NO_SUCH_TIMEZONE;
c0240cb1 637 tmp = NULL;
cacce04b
ER
638diff -up ./ext/date/lib/timelib.m4.systzdata ./ext/date/lib/timelib.m4
639--- ./ext/date/lib/timelib.m4.systzdata 2017-08-22 09:40:38.000000000 +0200
640+++ ./ext/date/lib/timelib.m4 2017-08-22 11:32:29.357799927 +0200
641@@ -81,3 +81,16 @@ io.h
c0240cb1 642
643 dnl Check for strtoll, atoll
3d8669ae 644 AC_CHECK_FUNCS(strtoll atoll strftime gettimeofday)
c0240cb1 645+
646+PHP_ARG_WITH(system-tzdata, for use of system timezone data,
647+[ --with-system-tzdata[=DIR] to specify use of system timezone data],
648+no, no)
649+
650+if test "$PHP_SYSTEM_TZDATA" != "no"; then
651+ AC_DEFINE(HAVE_SYSTEM_TZDATA, 1, [Define if system timezone data is used])
652+
653+ if test "$PHP_SYSTEM_TZDATA" != "yes"; then
654+ AC_DEFINE_UNQUOTED(HAVE_SYSTEM_TZDATA_PREFIX, "$PHP_SYSTEM_TZDATA",
655+ [Define for location of system timezone data])
656+ fi
657+fi
This page took 0.121047 seconds and 4 git commands to generate.