@@ -7,19 +7,28 @@ package kotlinx.datetime.internal
77
88internal class TzdbOnFilesystem (defaultTzdbPath : Path ? = null ): TimeZoneDatabase {
99
10- private val tzdbPath = tzdbPaths(defaultTzdbPath).find {
11- it.chaseSymlinks().check()?.isDirectory == true
10+ internal val tzdbPath = tzdbPaths(defaultTzdbPath).find { path ->
11+ tabPaths.any { path.containsFile(it) }
1212 } ? : throw IllegalStateException (" Could not find the path to the timezone database" )
1313
14- override fun rulesForId (id : String ): TimeZoneRules =
15- readTzFile(tzdbPath.resolve(Path .fromString(id)).readBytes()).toTimeZoneRules()
14+ override fun rulesForId (id : String ): TimeZoneRules {
15+ val idAsPath = Path .fromString(id)
16+ require(! idAsPath.isAbsolute) { " Timezone ID '$idAsPath ' must not begin with a '/'" }
17+ require(idAsPath.components.none { it == " .." }) { " '$idAsPath ' must not contain '..' as a component" }
18+ val file = Path (tzdbPath.isAbsolute, tzdbPath.components + idAsPath.components)
19+ val contents = file.readBytes() ? : throw RuntimeException (" File '$file ' not found" )
20+ return readTzFile(contents).toTimeZoneRules()
21+ }
1622
1723 override fun availableTimeZoneIds (): Set <String > = buildSet {
18- tzdbPath.traverseDirectory (exclude = tzdbUnneededFiles) { add(it.toString()) }
24+ tzdbPath.tryTraverseDirectory (exclude = tzdbUnneededFiles) { add(it.toString()) }
1925 }
2026
2127}
2228
29+ // taken from https://github.com/tzinfo/tzinfo/blob/9953fc092424d55deaea2dcdf6279943f3495724/lib/tzinfo/data_sources/zoneinfo_data_source.rb#L354
30+ private val tabPaths = listOf (" zone1970.tab" , " zone.tab" , " tab/zone_sun.tab" )
31+
2332/* * The files that sometimes lie in the `zoneinfo` directory but aren't actually time zones. */
2433private val tzdbUnneededFiles = setOf (
2534 // taken from https://github.com/tzinfo/tzinfo/blob/9953fc092424d55deaea2dcdf6279943f3495724/lib/tzinfo/data_sources/zoneinfo_data_source.rb#L88C29-L97C21
@@ -51,7 +60,7 @@ internal fun tzdbPaths(defaultTzdbPath: Path?) = sequence {
5160
5261// taken from https://github.com/HowardHinnant/date/blob/ab37c362e35267d6dee02cb47760f9e9c669d3be/src/tz.cpp#L3951-L3952
5362internal fun pathToSystemDefault (): Pair <Path , Path >? {
54- val info = Path ( true , listOf ( " etc" , " localtime" )).chaseSymlinks()
63+ val info = chaseSymlinks( " / etc/ localtime" ) ? : return null
5564 val i = info.components.indexOf(" zoneinfo" )
5665 if (! info.isAbsolute || i == - 1 || i == info.components.size - 1 ) return null
5766 return Pair (
0 commit comments