@Override
    public TimeZone nearestTimeZone(final Location searchLocation) {
        LinkedList<TwoDimTreeNode> nodeStack = new LinkedList<>();
        Set<TwoDimTreeNode> attendedNodes = new HashSet<>();

        Location bestGuess = traverseTheTree(nodeStack, root, searchLocation, attendedNodes);
        double bestDistance = centralAngle(searchLocation, bestGuess);

        while (!nodeStack.isEmpty()) {
            int layer = (nodeStack.size() - 1) % 2;
            TwoDimTreeNode lastNode = nodeStack.pollLast();
            Location newBest = null;

            if (hypersphereAndHyperplaneIntersection(
                    searchLocation.getCoordinates()[layer],
                    lastNode.getData().getCoordinates()[layer],
                    bestDistance)
                ) {
                if (lastNode.getLeft() != null && !attendedNodes.contains(lastNode.getLeft())) {
                    nodeStack.add(lastNode);
                    newBest = traverseTheTree(nodeStack, lastNode.getLeft(), searchLocation, attendedNodes);
                } else if (lastNode.getRight() != null && !attendedNodes.contains(lastNode.getRight())) {
                    nodeStack.add(lastNode);
                    newBest = traverseTheTree(nodeStack, lastNode.getRight(), searchLocation, attendedNodes);
                }
            }

            if (newBest != null) {
                double newBestDistance = centralAngle(searchLocation, newBest);

                if (newBestDistance < bestDistance) {
                    bestDistance = newBestDistance;
                    bestGuess = newBest;
                }
            }
        }

        return TimeZone.getTimeZone(bestGuess.getZone());
    }