mirror of
https://github.com/dlang/phobos.git
synced 2025-04-28 14:10:30 +03:00
On Android, update std.datetime to extract timezone data and some other fixes.
This commit is contained in:
parent
679298a7a9
commit
10c01a0614
6 changed files with 159 additions and 61 deletions
141
std/datetime.d
141
std/datetime.d
|
@ -28494,7 +28494,12 @@ public:
|
|||
}
|
||||
|
||||
|
||||
version(Posix)
|
||||
version(Android)
|
||||
{
|
||||
// Android concatenates all time zone data into a single file and stores it here.
|
||||
enum defaultTZDatabaseDir = "/system/usr/share/zoneinfo/";
|
||||
}
|
||||
else version(Posix)
|
||||
{
|
||||
/++
|
||||
The default directory where the TZ Database files are. It's empty
|
||||
|
@ -28549,13 +28554,22 @@ public:
|
|||
enforce(tzDatabaseDir.exists(), new DateTimeException(format("Directory %s does not exist.", tzDatabaseDir)));
|
||||
enforce(tzDatabaseDir.isDir, new DateTimeException(format("%s is not a directory.", tzDatabaseDir)));
|
||||
|
||||
const file = asNormalizedPath(chainPath(tzDatabaseDir, name)).to!string;
|
||||
version(Android)
|
||||
{
|
||||
auto tzfileOffset = name in tzdataIndex(tzDatabaseDir);
|
||||
enforce(tzfileOffset, new DateTimeException(format("The time zone %s is not listed.", name)));
|
||||
string tzFilename = separate_index ? "zoneinfo.dat" : "tzdata";
|
||||
const file = asNormalizedPath(chainPath(tzDatabaseDir, tzFilename)).to!string;
|
||||
}
|
||||
else
|
||||
const file = asNormalizedPath(chainPath(tzDatabaseDir, name)).to!string;
|
||||
|
||||
enforce(file.exists(), new DateTimeException(format("File %s does not exist.", file)));
|
||||
enforce(file.isFile, new DateTimeException(format("%s is not a file.", file)));
|
||||
|
||||
auto tzFile = File(file);
|
||||
immutable gmtZone = file.representation().canFind("GMT");
|
||||
version(Android) tzFile.seek(*tzfileOffset);
|
||||
immutable gmtZone = name.representation().canFind("GMT");
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -28739,8 +28753,16 @@ public:
|
|||
|
||||
auto posixEnvStr = tzFile.readln().strip();
|
||||
|
||||
_enforceValidTZFile(tzFile.readln().strip().empty);
|
||||
_enforceValidTZFile(tzFile.eof);
|
||||
version(Android)
|
||||
{
|
||||
// Android uses a single file for all timezone data, so the file
|
||||
// doesn't end here.
|
||||
}
|
||||
else
|
||||
{
|
||||
_enforceValidTZFile(tzFile.readln().strip().empty);
|
||||
_enforceValidTZFile(tzFile.eof);
|
||||
}
|
||||
|
||||
|
||||
auto transitionTypes = new TransitionType*[](tempTTInfos.length);
|
||||
|
@ -28889,20 +28911,31 @@ public:
|
|||
|
||||
auto timezones = appender!(string[])();
|
||||
|
||||
foreach(DirEntry dentry; dirEntries(tzDatabaseDir, SpanMode.depth))
|
||||
version(Android)
|
||||
{
|
||||
if(dentry.isFile)
|
||||
import std.algorithm : copy, filter;
|
||||
tzdataIndex(tzDatabaseDir)
|
||||
.byKey
|
||||
.filter!(a => a.startsWith(subName))
|
||||
.copy(timezones);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach(DirEntry dentry; dirEntries(tzDatabaseDir, SpanMode.depth))
|
||||
{
|
||||
auto tzName = dentry.name[tzDatabaseDir.length .. $];
|
||||
|
||||
if(!tzName.extension().empty ||
|
||||
!tzName.startsWith(subName) ||
|
||||
tzName == "+VERSION")
|
||||
if(dentry.isFile)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
auto tzName = dentry.name[tzDatabaseDir.length .. $];
|
||||
|
||||
timezones.put(tzName);
|
||||
if(!tzName.extension().empty ||
|
||||
!tzName.startsWith(subName) ||
|
||||
tzName == "+VERSION")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
timezones.put(tzName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28932,6 +28965,8 @@ public:
|
|||
foreach(tzName; tzNames)
|
||||
assertNotThrown!DateTimeException(testPTZSuccess(tzName));
|
||||
|
||||
// No timezone directories on Android, just a single tzdata file
|
||||
version(Android) {} else
|
||||
foreach(DirEntry dentry; dirEntries(defaultTZDatabaseDir, SpanMode.depth))
|
||||
{
|
||||
if(dentry.isFile)
|
||||
|
@ -29161,6 +29196,77 @@ private:
|
|||
_hasDST = hasDST;
|
||||
}
|
||||
|
||||
// Android concatenates the usual timezone directories into a single file,
|
||||
// tzdata, along with an index to jump to each timezone's offset. In older
|
||||
// versions of Android, the index was stored in a separate file, zoneinfo.idx,
|
||||
// whereas now it's stored at the beginning of tzdata.
|
||||
version(Android)
|
||||
{
|
||||
// Keep track of whether there's a separate index, zoneinfo.idx. Only
|
||||
// check this after calling tzdataIndex, as it's initialized there.
|
||||
static shared bool separate_index;
|
||||
|
||||
// Extracts the name of each time zone and the offset where its data is
|
||||
// located in the tzdata file from the index and caches it for later.
|
||||
static const(uint[string]) tzdataIndex(string tzDir)
|
||||
{
|
||||
import std.concurrency : initOnce;
|
||||
|
||||
static __gshared uint[string] _tzIndex;
|
||||
|
||||
// _tzIndex is initialized once and then shared across all threads.
|
||||
initOnce!_tzIndex(
|
||||
{
|
||||
import std.conv : to;
|
||||
import std.format : format;
|
||||
import std.path : asNormalizedPath, chainPath;
|
||||
|
||||
enum indexEntrySize = 52;
|
||||
const combinedFile = asNormalizedPath(chainPath(tzDir, "tzdata")).to!string;
|
||||
const indexFile = asNormalizedPath(chainPath(tzDir, "zoneinfo.idx")).to!string;
|
||||
File tzFile;
|
||||
uint indexEntries, dataOffset;
|
||||
uint[string] initIndex;
|
||||
|
||||
// Check for the combined file tzdata, which stores the index
|
||||
// and the time zone data together.
|
||||
if(combinedFile.exists() && combinedFile.isFile)
|
||||
{
|
||||
tzFile = File(combinedFile);
|
||||
_enforceValidTZFile(readVal!(char[])(tzFile, 6) == "tzdata");
|
||||
auto tzDataVersion = readVal!(char[])(tzFile, 6);
|
||||
_enforceValidTZFile(tzDataVersion[5] == '\0');
|
||||
|
||||
uint indexOffset = readVal!uint(tzFile);
|
||||
dataOffset = readVal!uint(tzFile);
|
||||
readVal!uint(tzFile);
|
||||
|
||||
indexEntries = (dataOffset - indexOffset)/indexEntrySize;
|
||||
separate_index = false;
|
||||
}
|
||||
else if(indexFile.exists() && indexFile.isFile)
|
||||
{
|
||||
tzFile = File(indexFile);
|
||||
indexEntries = to!(uint)(tzFile.size/indexEntrySize);
|
||||
separate_index = true;
|
||||
}
|
||||
else
|
||||
throw new DateTimeException(format("Both timezone files %s and %s do not exist.",
|
||||
combinedFile, indexFile));
|
||||
|
||||
foreach(Unused; 0 .. indexEntries) {
|
||||
string tzName = to!string(readVal!(char[])(tzFile, 40).ptr);
|
||||
uint tzOffset = readVal!uint(tzFile);
|
||||
readVal!(uint[])(tzFile, 2);
|
||||
initIndex[tzName] = dataOffset + tzOffset;
|
||||
}
|
||||
initIndex.rehash;
|
||||
return initIndex;
|
||||
}());
|
||||
return _tzIndex;
|
||||
}
|
||||
}
|
||||
|
||||
/// List of times when the utc offset changes.
|
||||
immutable Transition[] _transitions;
|
||||
|
||||
|
@ -29655,7 +29761,10 @@ else version(Posix)
|
|||
import core.sys.posix.stdlib : setenv;
|
||||
import core.sys.posix.time : tzset;
|
||||
|
||||
auto value = asNormalizedPath(chainPath(PosixTimeZone.defaultTZDatabaseDir, tzDatabaseName));
|
||||
version(Android)
|
||||
auto value = asNormalizedPath(tzDatabaseName);
|
||||
else
|
||||
auto value = asNormalizedPath(chainPath(PosixTimeZone.defaultTZDatabaseDir, tzDatabaseName));
|
||||
setenv("TZ", value.tempCString(), 1);
|
||||
tzset();
|
||||
}
|
||||
|
|
|
@ -664,15 +664,12 @@ unittest
|
|||
alloc.reallocate(b, 20);
|
||||
alloc.deallocate(b);
|
||||
|
||||
import std.file : deleteme, remove;
|
||||
import std.stdio : File;
|
||||
import std.range : walkLength;
|
||||
version(Posix)
|
||||
auto f = "/tmp/dlang.std.experimental.allocator.stats_collector.txt";
|
||||
version(Windows)
|
||||
{
|
||||
import std.process: environment;
|
||||
auto f = environment.get("temp") ~ r"\dlang.std.experimental.allocator.stats_collector.txt";
|
||||
}
|
||||
|
||||
auto f = deleteme ~ "-dlang.std.experimental.allocator.stats_collector.txt";
|
||||
scope(exit) remove(f);
|
||||
Allocator.reportPerCallStatistics(File(f, "w"));
|
||||
alloc.reportStatistics(File(f, "a"));
|
||||
assert(File(f).byLine.walkLength == 22);
|
||||
|
|
|
@ -3118,6 +3118,7 @@ private void copyImpl(const(char)[] f, const(char)[] t, const(FSChar)* fromz, co
|
|||
else version(Posix)
|
||||
{
|
||||
import core.stdc.stdio;
|
||||
static import std.conv;
|
||||
|
||||
immutable fd = core.sys.posix.fcntl.open(fromz, O_RDONLY);
|
||||
cenforce(fd != -1, f, fromz);
|
||||
|
@ -3158,7 +3159,7 @@ private void copyImpl(const(char)[] f, const(char)[] t, const(FSChar)* fromz, co
|
|||
size -= toxfer;
|
||||
}
|
||||
if (preserve)
|
||||
cenforce(fchmod(fdw, statbuf.st_mode) == 0, f, fromz);
|
||||
cenforce(fchmod(fdw, std.conv.to!mode_t(statbuf.st_mode)) == 0, f, fromz);
|
||||
}
|
||||
|
||||
cenforce(core.sys.posix.unistd.close(fdw) != -1, f, fromz);
|
||||
|
@ -3902,6 +3903,11 @@ string tempDir() @trusted
|
|||
DWORD len = GetTempPathW(buf.length, buf.ptr);
|
||||
if (len) cache = toUTF8(buf[0 .. len]);
|
||||
}
|
||||
else version(Android)
|
||||
{
|
||||
// Don't check for a global temporary directory as
|
||||
// Android doesn't have one.
|
||||
}
|
||||
else version(Posix)
|
||||
{
|
||||
import std.process : environment;
|
||||
|
|
24
std/format.d
24
std/format.d
|
@ -1793,9 +1793,7 @@ unittest
|
|||
formatTest( to!( const T)(5.5), "5.5" );
|
||||
formatTest( to!(immutable T)(5.5), "5.5" );
|
||||
|
||||
// bionic doesn't support lower-case string formatting of nan yet
|
||||
version(CRuntime_Bionic) { formatTest( T.nan, "NaN" ); }
|
||||
else { formatTest( T.nan, "nan" ); }
|
||||
formatTest( T.nan, "nan" );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3722,13 +3720,6 @@ unittest
|
|||
|| stream.data == "1.67 -0X1.47AE147AE147BP+0 nan", // MSVCRT 14+ (VS 2015)
|
||||
stream.data);
|
||||
}
|
||||
else version (CRuntime_Bionic)
|
||||
{
|
||||
// bionic doesn't support hex formatting of floating point numbers
|
||||
// or lower-case string formatting of nan yet, but it was committed
|
||||
// recently (April 2014):
|
||||
// https://code.google.com/p/android/issues/detail?id=64886
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(stream.data == "1.67 -0X1.47AE147AE147BP+0 nan",
|
||||
|
@ -3757,12 +3748,6 @@ unittest
|
|||
version (CRuntime_Microsoft)
|
||||
assert(stream.data == "0x1.51eb85p+0 0X1.B1EB86P+2"
|
||||
|| stream.data == "0x1.51eb851eb851fp+0 0X1.B1EB860000000P+2"); // MSVCRT 14+ (VS 2015)
|
||||
else version (CRuntime_Bionic)
|
||||
{
|
||||
// bionic doesn't support hex formatting of floating point numbers,
|
||||
// but it was committed recently (April 2014):
|
||||
// https://code.google.com/p/android/issues/detail?id=64886
|
||||
}
|
||||
else
|
||||
assert(stream.data == "0x1.51eb851eb851fp+0 0X1.B1EB86P+2");
|
||||
stream.clear();
|
||||
|
@ -6118,13 +6103,6 @@ unittest
|
|||
else version (CRuntime_Microsoft)
|
||||
assert(s == "1.67 -0X1.47AE14P+0 nan"
|
||||
|| s == "1.67 -0X1.47AE147AE147BP+0 nan", s); // MSVCRT 14+ (VS 2015)
|
||||
else version (CRuntime_Bionic)
|
||||
{
|
||||
// bionic doesn't support hex formatting of floating point numbers
|
||||
// or lower-case string formatting of nan yet, but it was committed
|
||||
// recently (April 2014):
|
||||
// https://code.google.com/p/android/issues/detail?id=64886
|
||||
}
|
||||
else
|
||||
assert(s == "1.67 -0X1.47AE147AE147BP+0 nan", s);
|
||||
|
||||
|
|
|
@ -724,10 +724,10 @@ private bool isExecutable(in char[] path) @trusted nothrow @nogc //TODO: @safe
|
|||
version (Posix) unittest
|
||||
{
|
||||
import std.algorithm;
|
||||
auto unamePath = searchPathFor("uname");
|
||||
assert (!unamePath.empty);
|
||||
assert (unamePath[0] == '/');
|
||||
assert (unamePath.endsWith("uname"));
|
||||
auto lsPath = searchPathFor("ls");
|
||||
assert (!lsPath.empty);
|
||||
assert (lsPath[0] == '/');
|
||||
assert (lsPath.endsWith("ls"));
|
||||
auto unlikely = searchPathFor("lkmqwpoialhggyaofijadsohufoiqezm");
|
||||
assert (unlikely is null, "Are you kidding me?");
|
||||
}
|
||||
|
@ -1474,7 +1474,12 @@ unittest // tryWait() and kill()
|
|||
TestScript prog = "while true; do sleep 1; done";
|
||||
}
|
||||
auto pid = spawnProcess(prog.path);
|
||||
Thread.sleep(dur!"seconds"(1));
|
||||
// Android appears to automatically kill sleeping processes very quickly,
|
||||
// so shorten the wait before killing here.
|
||||
version (Android)
|
||||
Thread.sleep(dur!"msecs"(5));
|
||||
else
|
||||
Thread.sleep(dur!"seconds"(1));
|
||||
kill(pid);
|
||||
version (Windows) assert (wait(pid) == 1);
|
||||
else version (Posix) assert (wait(pid) == -SIGTERM);
|
||||
|
@ -2147,6 +2152,10 @@ unittest
|
|||
"echo|set /p=%~1
|
||||
echo|set /p=%~2 1>&2
|
||||
exit 123";
|
||||
else version (Android) TestScript prog =
|
||||
`echo -n $1
|
||||
echo -n $2 >&2
|
||||
exit 123`;
|
||||
else version (Posix) TestScript prog =
|
||||
`printf '%s' $1
|
||||
printf '%s' $2 >&2
|
||||
|
@ -2239,17 +2248,15 @@ $(LREF nativeShell).
|
|||
*/
|
||||
@property string userShell() @safe
|
||||
{
|
||||
version (Windows) return environment.get("COMSPEC", "cmd.exe");
|
||||
else version (Android) return environment.get("SHELL", "/system/bin/sh");
|
||||
else version (Posix) return environment.get("SHELL", "/bin/sh");
|
||||
version (Windows) return environment.get("COMSPEC", nativeShell);
|
||||
else version (Posix) return environment.get("SHELL", nativeShell);
|
||||
}
|
||||
|
||||
/**
|
||||
The platform-specific native shell path.
|
||||
|
||||
On Windows, this function returns $(D "cmd.exe").
|
||||
|
||||
On POSIX, $(D nativeShell) returns $(D "/bin/sh").
|
||||
This function returns $(D "cmd.exe") on Windows, $(D "/bin/sh") on POSIX, and
|
||||
$(D "/system/bin/sh") on Android.
|
||||
*/
|
||||
@property string nativeShell() @safe @nogc pure nothrow
|
||||
{
|
||||
|
@ -2344,8 +2351,7 @@ private struct TestScript
|
|||
else version (Posix)
|
||||
{
|
||||
auto ext = "";
|
||||
version(Android) auto firstLine = "#!" ~ userShell;
|
||||
else auto firstLine = "#!/bin/sh";
|
||||
auto firstLine = "#!" ~ nativeShell;
|
||||
}
|
||||
path = uniqueTempPath()~ext;
|
||||
std.file.write(path, firstLine~std.ascii.newline~code~std.ascii.newline);
|
||||
|
|
|
@ -466,9 +466,11 @@ class Protocol
|
|||
}
|
||||
|
||||
|
||||
// Skip this test on Android because getprotobyname/number are
|
||||
// unimplemented in bionic.
|
||||
version(CRuntime_Bionic) {} else
|
||||
unittest
|
||||
{
|
||||
// getprotobyname,number are unimplemented in bionic
|
||||
softUnittest({
|
||||
Protocol proto = new Protocol;
|
||||
assert(proto.getProtocolByType(ProtocolType.TCP));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue