Skip to content

Commit 3581244

Browse files
authored
Fix #11000: getInstalledTZNames can't handle a trailing slash. (#11011)
Normally, on most systems, PosixTimeZone.getInstalledTZNames uses /usr/share/zoneinfo/ for the time zone database directory, but it can be given a different directory, and it will use what TZDIR has been set to if it's been set (since the function used as the default argument checks TZDIR). So, while the bug refers specifically to TZDIR, that's just one way that the alternate time zone database directory can be passed to getInstalledTZNames. If getInstalledTZNames is given a directory name which does not end with a slash, then the logic for stripping off the time zone database directory name from the full paths to each time zone file leaves a slash on the front of the time zone name, which then doesn't work correctly, since it's not supposed to start with a slash. So, this fixes it so that the time zone names don't end up with slashes due to how many slashes the time zone database directory name does or doesn't have. I tested the subName argument as part of this in case it mattered and was somewhat horrified to realize that it had not actually been tested by any of the existing tests. So, I guess that I screwed up on that count. Fortunately, it seems to work correctly.
1 parent 789fd8d commit 3581244

1 file changed

Lines changed: 117 additions & 0 deletions

File tree

std/datetime/timezone.d

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2474,6 +2474,10 @@ public:
24742474
{
24752475
auto tzName = de.name[tzDatabaseDir.length .. $];
24762476

2477+
// For the case where tzDatabaseDir does not have a trailing slash.
2478+
if (tzName.length > 1 && tzName[0] == '/')
2479+
tzName = tzName[1 .. $];
2480+
24772481
if (!tzName.extension().empty ||
24782482
!tzName.startsWith(subName) ||
24792483
baseName(tzName) == "leapseconds" ||
@@ -2537,6 +2541,119 @@ public:
25372541
}
25382542
}
25392543

2544+
// https://github.com/dlang/phobos/issues/11000
2545+
version (Posix) @safe unittest
2546+
{
2547+
version (Android)
2548+
{}
2549+
else
2550+
{
2551+
import std.algorithm.searching : canFind;
2552+
import std.file : chdir, copy, exists, getcwd, mkdirRecurse, rmdirRecurse, tempDir;
2553+
import std.path : buildPath;
2554+
2555+
immutable baseDir = buildPath(tempDir, "tztest");
2556+
immutable tzDir = buildPath(baseDir, "tz");
2557+
immutable tzDirSlash = buildPath(baseDir, "tz/");
2558+
immutable tzDirDoubleSlash = buildPath(baseDir, "tz//");
2559+
assert(tzDirSlash[$ - 1] == '/'); // just in case buildPath ever strips the slash
2560+
2561+
scope(failure) if (baseDir.exists) rmdirRecurse(baseDir);
2562+
2563+
mkdirRecurse(buildPath(tzDir, "America"));
2564+
mkdirRecurse(buildPath(tzDir, "Europe"));
2565+
2566+
copy(buildPath(defaultTZDatabaseDir, "America/Denver"),
2567+
buildPath(tzDir, "America/Denver"));
2568+
copy(buildPath(defaultTZDatabaseDir, "America/Denver"),
2569+
buildPath(tzDir, "America/Chicago"));
2570+
copy(buildPath(defaultTZDatabaseDir, "Europe/London"),
2571+
buildPath(tzDir, "Europe/London"));
2572+
copy(buildPath(defaultTZDatabaseDir, "UTC"),
2573+
buildPath(tzDir, "UTC"));
2574+
2575+
foreach (dir; [tzDir, tzDirSlash, tzDirDoubleSlash])
2576+
{
2577+
{
2578+
auto names = getInstalledTZNames("", dir);
2579+
assert(names.length == 4);
2580+
assert(names.canFind("America/Denver"));
2581+
assert(names.canFind("America/Chicago"));
2582+
assert(names.canFind("Europe/London"));
2583+
assert(names.canFind("UTC"));
2584+
}
2585+
{
2586+
auto names = getInstalledTZNames("America", dir);
2587+
assert(names.length == 2);
2588+
assert(names.canFind("America/Denver"));
2589+
assert(names.canFind("America/Chicago"));
2590+
}
2591+
}
2592+
2593+
immutable cwd = getcwd();
2594+
scope(exit) chdir(cwd);
2595+
2596+
chdir(baseDir);
2597+
foreach (dir; ["tz", "tz/", "tz///"])
2598+
{
2599+
{
2600+
auto names = getInstalledTZNames("", dir);
2601+
assert(names.length == 4);
2602+
assert(names.canFind("America/Denver"));
2603+
assert(names.canFind("America/Chicago"));
2604+
assert(names.canFind("Europe/London"));
2605+
assert(names.canFind("UTC"));
2606+
}
2607+
{
2608+
auto names = getInstalledTZNames("America", dir);
2609+
assert(names.length == 2);
2610+
assert(names.canFind("America/Denver"));
2611+
assert(names.canFind("America/Chicago"));
2612+
}
2613+
}
2614+
2615+
immutable other = buildPath(baseDir, "other");
2616+
mkdirRecurse(other);
2617+
chdir(other);
2618+
foreach (dir; ["../tz", "../tz/", "..///tz/////"])
2619+
{
2620+
{
2621+
auto names = getInstalledTZNames("", dir);
2622+
assert(names.length == 4);
2623+
assert(names.canFind("America/Denver"));
2624+
assert(names.canFind("America/Chicago"));
2625+
assert(names.canFind("Europe/London"));
2626+
assert(names.canFind("UTC"));
2627+
}
2628+
{
2629+
auto names = getInstalledTZNames("America", dir);
2630+
assert(names.length == 2);
2631+
assert(names.canFind("America/Denver"));
2632+
assert(names.canFind("America/Chicago"));
2633+
}
2634+
}
2635+
2636+
chdir(tzDir);
2637+
foreach (dir; [".", "./", ".///"])
2638+
{
2639+
{
2640+
auto names = getInstalledTZNames("", dir);
2641+
assert(names.length == 4);
2642+
assert(names.canFind("America/Denver"));
2643+
assert(names.canFind("America/Chicago"));
2644+
assert(names.canFind("Europe/London"));
2645+
assert(names.canFind("UTC"));
2646+
}
2647+
{
2648+
auto names = getInstalledTZNames("America", dir);
2649+
assert(names.length == 2);
2650+
assert(names.canFind("America/Denver"));
2651+
assert(names.canFind("America/Chicago"));
2652+
}
2653+
}
2654+
}
2655+
}
2656+
25402657

25412658
private:
25422659

0 commit comments

Comments
 (0)