Repository: microsoft/rego-cpp (language: python)

Issue:
`uuid` builtins



Discussion / hints:

Fix patch:
diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml
index 40efa87c..d8984087 100644
--- a/.github/workflows/build_wheels.yml
+++ b/.github/workflows/build_wheels.yml
@@ -19,7 +19,7 @@ jobs:
           git config --global --add remote.origin.fetch +refs/pull/*/merge:refs/remotes/pull/*
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v2.18.1
+        uses: pypa/cibuildwheel@v2.21.1
         env:
           CIBW_BUILD: ${{ matrix.python }}-win*
           CIBW_ENVIRONMENT: >
@@ -61,7 +61,7 @@ jobs:
           export PATH=$(python3 -m site --user-base)/bin:$PATH
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v2.18.1
+        uses: pypa/cibuildwheel@v2.21.1
         env:
           CIBW_BUILD: ${{ matrix.python }}-macosx*
           CIBW_ARCHS_MACOS: arm64
@@ -90,7 +90,7 @@ jobs:
           git config --global --add remote.origin.fetch +refs/pull/*/merge:refs/remotes/pull/*
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v2.18.1
+        uses: pypa/cibuildwheel@v2.21.1
         env:
           CIBW_BUILD: ${{ matrix.python }}-manylinux*
           CIBW_BEFORE_ALL: yum makecache && yum -y install devtoolset-10-libatomic-devel
@@ -119,7 +119,7 @@ jobs:
           git config --global --add remote.origin.fetch +refs/pull/*/merge:refs/remotes/pull/*
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v2.18.1
+        uses: pypa/cibuildwheel@v2.21.1
         env:
           CIBW_BUILD: ${{ matrix.python }}-musllinux*
           CIBW_ENVIRONMENT: >
diff --git a/.github/workflows/pr_gate.yml b/.github/workflows/pr_gate.yml
index 6d122df8..004c8507 100644
--- a/.github/workflows/pr_gate.yml
+++ b/.github/workflows/pr_gate.yml
@@ -209,7 +209,7 @@ jobs:
       
       - name: CMake config
         run: |
-          cmake -B ${{github.workspace}}/build --preset release-opa
+          cmake -B ${{github.workspace}}/build --preset release-opa -DREGOCPP_TZDATA_PATH=${{github.workspace}}/build
       
       - name: CMake build
         working-directory: ${{github.workspace}}/build
diff --git a/CHANGELOG b/CHANGELOG
index 1199ca7a..1d6b13be 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,28 @@
 # Changelog
 
+## 2024-09-23 - Version 0.4.4
+Point release adding the `uuid`, `time`, and `walk` builtins.
+
+In order to add support for the `time` built-ins that worked cross-platform and with the widest
+range of C++ compilers, we had to introduce a dependency on the [`date`](https://github.com/HowardHinnant/date)
+library. This contains a full implementations of the `std::chrono` functionality which will eventually be
+supported across most c++ compilers as part of the STL. In particular, it provides the crucial Time Zone
+lookup functionalities required by the Rego `time` built-ins. The Time Zone lookup requires a valid
+[`tzdata`](https://www.iana.org/time-zones) database from the IANA. This can either be configured to use the local
+system TZ data (default for Linux variants) or a manual database (default for Windows). If the `REGOCPP_USE_MANUAL_TZDATA` flag
+is set, then the project will download the latest database and install it as part of its build process.
+
+**New Features**
+- Added the `uuid` built-ins
+- Added the `time` built-ins
+- Added the `walk` built-in
+- It is now possible for `BuiltInDef` implementations to cache values or otherwise maintain state. To facilitate this,
+  a new virtual `clear()` method has been added which will be called at the start of each query evaluation.
+
+**Bug Fixes**
+- Fixed a bug with adding zero to negative integers.
+
+
 ## 2024-09-10 - Version 0.4.3
 Point release updating to Rego v0.68.0
 
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1d45bafe..878a365e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -69,6 +69,7 @@ option(REGOCPP_COPY_EXAMPLES "Specifies whether to copy the examples to the inst
 option(REGOCPP_USE_CXX17 "Specifies whether to use C++17" OFF)
 option(REGOCPP_ACTION_METRICS "Specifies whether to metricate Trieste Actions" OFF)
 option(REGOCPP_CLEAN_INSTALL "Whether the install directory should be cleaned before install" OFF)
+option(REGOCPP_USE_MANUAL_TZDATA "Whether to use the manual tzdata" OFF)
 set(REGOCPP_SANITIZE "" CACHE STRING "Argument to pass to sanitize (disabled by default)")
 
 if(REGOCPP_USE_CXX17)
@@ -90,14 +91,58 @@ FetchContent_Declare(
     GIT_SHALLOW FALSE
 )
 
+FetchContent_Declare(
+  date
+  GIT_REPOSITORY https://github.com/HowardHinnant/date.git
+  GIT_TAG 51ce7e131079c061533d741be5fe7cca57f2faac
+)
+
 FetchContent_Declare(
   trieste
   GIT_REPOSITORY https://github.com/microsoft/trieste
-  GIT_TAG        467157a167d2e929095498089f29fc9419f4ef11
+  GIT_TAG        70a4d5f79cb574070ff69b2517b422c5126e2024
 )
 
 FetchContent_MakeAvailable(cmake_utils)
 
+if(MSVC)
+  message("MSVC: must use manual TZ database")
+  set(REGOCPP_USE_MANUAL_TZDATA ON)
+endif()
+
+set(REGOCPP_TZDATA_PATH "${CMAKE_INSTALL_PREFIX}" CACHE STRING "Path to the tzdata directory")
+file(TO_CMAKE_PATH ${REGOCPP_TZDATA_PATH} REGOCPP_TZDATA_PATH)
+
+if(REGOCPP_USE_MANUAL_TZDATA)
+  set(MANUAL_TZ_DB ON)
+  set(USE_SYSTEM_TZ_DB OFF)
+  set(TZDATA_GZ_PATH "${CMAKE_CURRENT_BINARY_DIR}/tzdata-latest.tar.gz")
+  set(TZDATA_PATH "${CMAKE_CURRENT_BINARY_DIR}/tzdata")
+  if(NOT EXISTS ${TZDATA_PATH})
+    message("Downloading tzdata to ${TZDATA_GZ_PATH}")
+    # we need to download the TZ database and make it available
+    FILE(DOWNLOAD https://www.iana.org/time-zones/repository/tzdata-latest.tar.gz ${TZDATA_GZ_PATH} SHOW_PROGRESS)
+    message("Extracting ${TZDATA_GZ_PATH} to ${TZDATA_PATH}")
+    FILE(ARCHIVE_EXTRACT INPUT ${TZDATA_GZ_PATH} DESTINATION ${TZDATA_PATH})
+
+    # Copy the tzdata directory to the specified path.
+    message("Copying ${TZDATA_PATH} to ${REGOCPP_TZDATA_PATH}")
+    file(COPY ${TZDATA_PATH} DESTINATION ${REGOCPP_TZDATA_PATH})
+    if(MSVC)
+      message("MSVC: Copying windowsZones.xml to ${REGOCPP_TZDATA_PATH}/tzdata")
+      file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/templates/tzdata/windowsZones.xml DESTINATION ${REGOCPP_TZDATA_PATH}/tzdata)
+    endif()
+  endif()
+else()
+  message("Attempting to use system TZ database")
+  set(USE_SYSTEM_TZ_DB ON)
+  set(MANUAL_TZ_DB OFF)
+endif()
+
+set(BUILD_TZ_LIB ON)
+set(COMPILE_WITH_C_LOCALE ON)
+FetchContent_MakeAvailable_ExcludeFromAll(date)
+
 set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) 
 
 set(TRIESTE_USE_CXX17 ${REGOCPP_USE_CXX17})
@@ -195,11 +240,18 @@ if(REGOCPP_CLEAN_INSTALL)
     ]])
 endif()
 
+if(REGOCPP_USE_MANUAL_TZDATA)
+  # If the install depends on this, we want to include it in the install package.
+  INSTALL(DIRECTORY ${TZDATA_PATH} DESTINATION .)
+  if(MSVC)
+    INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/templates/tzdata/windowsZones.xml DESTINATION tzdata)
+  endif()
+endif()
 
 set(INSTALL_CONFIGDIR cmake)
 set(INSTALL_LIBDIR lib)
 set(INSTALL_INCLUDEDIR include)
-set(REGOCPP_INSTALL_TARGETS rego trieste json yaml snmalloc re2)
+set(REGOCPP_INSTALL_TARGETS rego trieste json yaml snmalloc re2 date-tz)
 
 if(TRIESTE_USE_SNMALLOC)
   list(APPEND REGOCPP_INSTALL_TARGETS snmalloc-new-override)
@@ -257,6 +309,6 @@ export(PACKAGE ${PROJECT_NAME})
 
 install(DIRECTORY include/ DESTINATION ${INSTALL_INCLUDEDIR})
 install(FILES 
-  ${CMAKE_CURRENT_BINARY_DIR}/src/version.h
+  ${CMAKE_CURRENT_BINARY_DIR}/src/version.h  
   DESTINATION ${INSTALL_INCLUDEDIR}/rego
 )
diff --git a/README.md b/README.md
index a6b6a7ee..97f93b3b 100644
--- a/README.md
+++ b/README.md
@@ -76,7 +76,7 @@ library from different langauages.
 
 ## Language Support
 
-At present we support v0.65.0 of Rego as defined by OPA, with the following grammar:
+We support v0.68.0 of Rego as defined by OPA, with the following grammar:
 
 ```ebnf
 module          = package { import } policy
@@ -150,8 +150,7 @@ LF     Line Feed
 
 ### Builtins
 
-At the moment only support a few builtins, but are actively working on adding
-all the standard builtins. The following builtins are currently supported:
+At the moment support the following builtins are available:
 
 - `aggregates`
 - `arrays`
@@ -165,12 +164,13 @@ all the standard builtins. The following builtins are currently supported:
 - `semver`
 - `sets`
 - `strings`
+- `time`
 - `types`
 - `units`
+- `uuid`
 - miscellaneous
     * `opa.runtime`
     * `print`
-    * `time.now_ns`
 
 ### Compatibility with the OPA Rego Go implementation
 
@@ -193,8 +193,6 @@ At present, we are **NOT** passing the following test suites in full:
 - `net*`
 - `planner-ir`
 - `providers-aws`
-- `time`
-- `walkbuiltin`
 
 ## Contributing
 
diff --git a/VERSION b/VERSION
index 70d5b25f..b300caa3 100644
--- a/VERSION
+++ b/VERSION
@@ -1,1 +1,1 @@
-0.4.3
\ No newline at end of file
+0.4.4
\ No newline at end of file
diff --git a/examples/rust/Cargo.toml b/examples/rust/Cargo.toml
index 80797eee..e0ea8fc6 100644
--- a/examples/rust/Cargo.toml
+++ b/examples/rust/Cargo.toml
@@ -6,5 +6,5 @@ edition = "2021"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-regorust = "0.4.0"
+regorust = "0.4.4"
 clap = { version = "4.0", features = ["derive"] }
\ No newline at end of file
diff --git a/include/rego/rego.hh b/include/rego/rego.hh
index 61072078..64551e23 100644
--- a/include/rego/rego.hh
+++ b/include/rego/rego.hh
@@ -206,7 +206,7 @@ namespace rego
   using BuiltIn = std::shared_ptr<BuiltInDef>;
 
   /** The function pointer to the behavior of the built-in. */
-  using BuiltInBehavior = Node (*)(const Nodes&);
+  using BuiltInBehavior = std::function<Node(const Nodes&)>;
 
   /**
    * This is a basic, non-optimized implementation of a big integer using
@@ -622,6 +622,18 @@ namespace rego
     /** The function which will be called when the built-in is evaluated. */
     BuiltInBehavior behavior;
 
+    /**
+     * Constructor.
+     */
+    BuiltInDef(Location name_, std::size_t arity_, BuiltInBehavior behavior_);
+
+    virtual ~BuiltInDef() = default;
+
+    /**
+     * Called to clear any persistent state or caching.
+     */
+    virtual void clear();
+
     /**
      * Creates a new built-in.
      *
@@ -672,8 +684,12 @@ namespace rego
      * @param version The Rego version.
      * @return The result of the built-in call.
      */
-    Node call(
-      const Location& name, const Location& version, const Nodes& args) const;
+    Node call(const Location& name, const Location& version, const Nodes& args);
+
+    /**
+     * Called to clear any persistent state or caching.
+     */
+    void clear();
 
     /**
      * Registers a built-in.
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index ab794dde..e40262e0 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,4 +1,5 @@
 configure_file("${CMAKE_CURRENT_SOURCE_DIR}/version.h.in" "${CMAKE_CURRENT_BINARY_DIR}/version.h" @ONLY)
+configure_file("${CMAKE_CURRENT_SOURCE_DIR}/builtins/tzdata.h.in" "${CMAKE_CURRENT_BINARY_DIR}/tzdata.h" @ONLY)
 
 set( SOURCES
 bigint.cc
@@ -59,6 +60,7 @@ builtins/strings.cc
 builtins/time.cc
 builtins/types.cc
 builtins/units.cc
+builtins/uuid.cc
 # third-party
 builtins/base64/base64.cpp
 )
@@ -71,11 +73,16 @@ if (REGOCPP_SPECULATIVE_LOAD_HARDENING)
   target_compile_options(rego PRIVATE -mllvm -x86-speculative-load-hardening)
 endif()
 
+if (MANUAL_TZ_DB)
+  target_compile_definitions(rego PUBLIC "MANUAL_TZ_DB")
+endif()
+
 target_link_libraries(rego
   PUBLIC
     trieste::trieste
     trieste::json
     trieste::yaml
+    date-tz
 )
 
 if(MSVC)
diff --git a/src/bigint.cc b/src/bigint.cc
index 2cd9e4fc..2c8042e1 100644
--- a/src/bigint.cc
+++ b/src/bigint.cc
@@ -132,6 +132,11 @@ namespace rego
 
   BigInt operator-(const BigInt& lhs, const BigInt& rhs)
   {
+    if (rhs.is_zero())
+    {
+      return lhs;
+    }
+
     bool negate = false;
     if (lhs.is_negative())
     {
@@ -173,11 +178,6 @@ namespace rego
       }
     }
 
-    if (rhs.is_zero())
-    {
-      return lhs;
-    }
-
     std::string difference =
       BigInt::subtract(lhs.digits(), rhs.digits(), negate);
     return BigInt(difference);
diff --git a/src/builtins.cc b/src/builtins.cc
index b2c3d94b..63670107 100644
--- a/src/builtins.cc
+++ b/src/builtins.cc
@@ -38,6 +38,13 @@ namespace
 
 namespace rego
 {
+  BuiltInDef::BuiltInDef(
+    Location name_, std::size_t arity_, BuiltInBehavior behavior_) :
+    name(name_), arity(arity_), behavior(behavior_)
+  {}
+
+  void BuiltInDef::clear() {}
+
   BuiltIn BuiltInDef::create(
     const Location& name, std::size_t arity, BuiltInBehavior behavior)
   {
@@ -46,6 +53,14 @@ namespace rego
 
   BuiltInsDef::BuiltInsDef() noexcept : m_strict_errors(false) {}
 
+  void BuiltInsDef::clear()
+  {
+    for (auto& builtin : m_builtins)
+    {
+      builtin.second->clear();
+    }
+  }
+
   bool BuiltInsDef::strict_errors() const
   {
     return m_strict_errors;
@@ -91,7 +106,7 @@ namespace rego
   }
 
   Node BuiltInsDef::call(
-    const Location& name, const Location& version, const Nodes& args) const
+    const Location& name, const Location& version, const Nodes& args)
   {
     if (!is_builtin(name))
     {
@@ -164,6 +179,7 @@ namespace rego
     register_builtins(builtins::time());
     register_builtins(builtins::types());
     register_builtins(builtins::units());
+    register_builtins(builtins::uuid());
 
     return *this;
   }
diff --git a/src/builtins/builtins.h b/src/builtins/builtins.h
index 9503deda..074a573e 100644
--- a/src/builtins/builtins.h
+++ b/src/builtins/builtins.h
@@ -21,5 +21,6 @@ namespace rego
     std::vector<BuiltIn> time();
     std::vector<BuiltIn> types();
     std::vector<BuiltIn> units();
+    std::vector<BuiltIn> uuid();
   }
 }
\ No newline at end of file
diff --git a/src/builtins/encoding.cc b/src/builtins/encoding.cc
index 19fba1c1..8ff824e2 100644
--- a/src/builtins/encoding.cc
+++ b/src/builtins/encoding.cc
@@ -380,7 +380,7 @@ namespace
     return result.ok ? True ^ "true" : False ^ "false";
   }
 
-  void urlquery_encode(std::ostringstream& encoded, Node x)
+  void do_urlquery_encode(std::ostringstream& encoded, Node x)
   {
     std::string x_json = get_string(x);
     std::string x_raw = json::unescape(strip_quotes(x_json));
@@ -407,7 +407,7 @@ namespace
     }
 
     std::ostringstream encoded;
-    urlquery_encode(encoded, x);
+    do_urlquery_encode(encoded, x);
 
     return JSONString ^ ('"' + json::escape(encoded.str()) + '"');
   }
@@ -430,7 +430,7 @@ namespace
       }
 
       std::ostringstream key_buf;
-      urlquery_encode(key_buf, maybe_key.node);
+      do_urlquery_encode(key_buf, maybe_key.node);
       std::string key_str = key_buf.str();
 
       auto maybe_value = unwrap(object->at(i) / Val, {JSONString, Array, Set});
@@ -450,7 +450,7 @@ namespace
       if (maybe_value.node == JSONString)
       {
         encoded << key_str << '=';
-        urlquery_encode(encoded, maybe_value.node);
+        do_urlquery_encode(encoded, maybe_value.node);
       }
       else if (maybe_value.node == Array || maybe_value.node == Set)
       {
@@ -469,7 +469,7 @@ namespace
             encoded << '&';
           }
           encoded << key_str << '=';
-          urlquery_encode(encoded, maybe_value.node);
+          do_urlquery_encode(encoded, maybe_value.node);
         }
       }
     }
@@ -477,7 +477,7 @@ namespace
     return JSONString ^ ('"' + json::escape(encoded.str()) + '"');
   }
 
-  std::optional<std::string> urlquery_decode(const std::string& encoded)
+  std::optional<std::string> maybe_urlquery_decode(const std::string& encoded)
   {
     std::ostringstream decoded;
     for (auto it = encoded.begin(); it != encoded.end(); ++it)
@@ -519,7 +519,7 @@ namespace
 
     std::string x_json = get_string(x);
     std::string x_raw = json::unescape(strip_quotes(x_json));
-    auto maybe_result = urlquery_decode(x_raw);
+    auto maybe_result = maybe_urlquery_decode(x_raw);
     if (maybe_result.has_value())
     {
       return JSONString ^ ('"' + json::escape(maybe_result.value()) + '"');
@@ -545,7 +545,7 @@ namespace
     {
       std::string item_value = x_raw.substr(start, end - start);
       size_t equal = item_value.find('=');
-      auto maybe_key = urlquery_decode(item_value.substr(0, equal));
+      auto maybe_key = maybe_urlquery_decode(item_value.substr(0, equal));
       if (!maybe_key.has_value())
       {
         return err(x, "invalid URL query string", EvalBuiltInError);
@@ -559,7 +559,7 @@ namespace
 
       if (equal != std::string::npos)
       {
-        auto maybe_value = urlquery_decode(item_value.substr(equal + 1));
+        auto maybe_value = maybe_urlquery_decode(item_value.substr(equal + 1));
 
         if (!maybe_value.has_value())
         {
diff --git a/src/builtins/numbers.cc b/src/builtins/numbers.cc
index b8c85fdd..14cf394f 100644
--- a/src/builtins/numbers.cc
+++ b/src/builtins/numbers.cc
@@ -6,7 +6,7 @@ namespace
 {
   using namespace rego;
 
-  Node abs(const Nodes& args)
+  Node abs_(const Nodes& args)
   {
     Node number = unwrap_arg(
       args, UnwrapOpt(0).types({Int, Float}).message("Not a number"));
@@ -35,7 +35,7 @@ namespace
     }
   }
 
-  Node ceil(const Nodes& args)
+  Node ceil_(const Nodes& args)
   {
     Node number = unwrap_arg(
       args, UnwrapOpt(0).types({Int, Float}).message("Not a number"));
@@ -55,7 +55,7 @@ namespace
     }
   }
 
-  Node floor(const Nodes& args)
+  Node floor_(const Nodes& args)
   {
     Node number = unwrap_arg(
       args, UnwrapOpt(0).types({Int, Float}).message("Not a number"));
@@ -75,7 +75,7 @@ namespace
     }
   }
 
-  Node round(const Nodes& args)
+  Node round_(const Nodes& args)
   {
     Node number = unwrap_arg(
       args, UnwrapOpt(0).types({Int, Float}).message("Not a number"));
@@ -241,10 +241,10 @@ namespace rego
     std::vector<BuiltIn> numbers()
     {
       return {
-        BuiltInDef::create(Location("abs"), 1, abs),
-        BuiltInDef::create(Location("ceil"), 1, ceil),
-        BuiltInDef::create(Location("floor"), 1, floor),
-        BuiltInDef::create(Location("round"), 1, round),
+        BuiltInDef::create(Location("abs"), 1, abs_),
+        BuiltInDef::create(Location("ceil"), 1, ceil_),
+        BuiltInDef::create(Location("floor"), 1, floor_),
+        BuiltInDef::create(Location("round"), 1, round_),
         BuiltInDef::create(Location("numbers.range"), 2, numbers_range),
         BuiltInDef::create(
           Location("numbers.range_step"), 3, numbers_range_step),
diff --git a/src/builtins/objects.cc b/src/builtins/objects.cc
index e623662d..8a74d1a3 100644
--- a/src/builtins/objects.cc
+++ b/src/builtins/objects.cc
@@ -150,7 +150,7 @@ namespace
     return value;
   }
 
-  Node remove(const Nodes& args)
+  Node remove_(const Nodes& args)
   {
     Node object =
       unwrap_arg(args, UnwrapOpt(0).func("object.remove").type(Object));
@@ -442,7 +442,7 @@ namespace rego
         BuiltInDef::create(Location("object.filter"), 2, filter),
         BuiltInDef::create(Location("object.get"), 3, get),
         BuiltInDef::create(Location("object.keys"), 1, keys),
-        BuiltInDef::create(Location("object.remove"), 2, remove),
+        BuiltInDef::create(Location("object.remove"), 2, remove_),
         BuiltInDef::create(Location("object.subset"), 2, subset),
         BuiltInDef::create(Location("object.union"), 2, union_),
         BuiltInDef::create(Location("object.union_n"), 1, union_n)};
diff --git a/src/builtins/strings.cc b/src/builtins/strings.cc
index 6ce795cd..24838010 100644
--- a/src/builtins/strings.cc
+++ b/src/builtins/strings.cc
@@ -267,7 +267,8 @@ namespace
     return Resolver::scalar(x_str);
   }
 
-  void replace(std::string& x, const std::string& old, const std::string& new_)
+  void do_replace(
+    std::string& x, const std::string& old, const std::string& new_)
   {
     auto pos = x.find(old);
     while (pos != x.npos)
@@ -300,7 +301,7 @@ namespace
     std::string x_str = get_string(x);
     std::string old_str = get_string(old);
     std::string new_str = get_string(new_);
-    replace(x_str, old_str, new_str);
+    do_replace(x_str, old_str, new_str);
 
     return Resolver::scalar(x_str);
   }
@@ -435,7 +436,7 @@ namespace
     return verbs;
   }
 
-  Node sprintf(const Nodes& args)
+  Node sprintf_(const Nodes& args)
   {
     Node format =
       unwrap_arg(args, UnwrapOpt(0).type(JSONString).func("sprintf"));
@@ -705,7 +706,7 @@ namespace
 
       std::string old_str = get_string(old_node);
       std::string new_str = get_string(new_node);
-      replace(value_str, old_str, new_str);
+      do_replace(value_str, old_str, new_str);
     }
 
     return JSONString ^ value_str;
@@ -781,7 +782,7 @@ namespace
     return JSONString ^ output.str();
   }
 
-  std::string trim(
+  std::string do_trim(
     const std::string& value, const std::string& cutset, bool left, bool right)
   {
     runestring value_runes = utf8_to_runestring(json::unescape(value));
@@ -829,7 +830,8 @@ namespace
       return cutset;
     }
 
-    return JSONString ^ trim(get_string(value), get_string(cutset), true, true);
+    return JSONString ^
+      do_trim(get_string(value), get_string(cutset), true, true);
   }
 
   Node trim_left(const Nodes& args)
@@ -848,7 +850,7 @@ namespace
     }
 
     return JSONString ^
-      trim(get_string(value), get_string(cutset), true, false);
+      do_trim(get_string(value), get_string(cutset), true, false);
   }
 
   Node trim_right(const Nodes& args)
@@ -867,7 +869,7 @@ namespace
     }
 
     return JSONString ^
-      trim(get_string(value), get_string(cutset), false, true);
+      do_trim(get_string(value), get_string(cutset), false, true);
   }
 
   Node trim_space(const Nodes& args)
@@ -879,7 +881,7 @@ namespace
       return value;
     }
 
-    return JSONString ^ trim(get_string(value), " \t\n\r\v\f", true, true);
+    return JSONString ^ do_trim(get_string(value), " \t\n\r\v\f", true, true);
   }
 
   Node trim_prefix(const Nodes& args)
@@ -987,7 +989,7 @@ namespace rego
         BuiltInDef::create(Location("upper"), 1, upper),
         BuiltInDef::create(Location("replace"), 3, replace),
         BuiltInDef::create(Location("split"), 2, split),
-        BuiltInDef::create(Location("sprintf"), 2, sprintf),
+        BuiltInDef::create(Location("sprintf"), 2, sprintf_),
         BuiltInDef::create(
           Location("strings.any_prefix_match"), 2, any_prefix_match),
         BuiltInDef::create(
diff --git a/src/builtins/time.cc b/src/builtins/time.cc
index 2252e9d1..afa8be6a 100644
--- a/src/builtins/time.cc
+++ b/src/builtins/time.cc
@@ -1,19 +1,717 @@
 #include "builtins.h"
+#include "date/date.h"
+#include "date/tz.h"
+#include "re2/stringpiece.h"
+#include "rego.hh"
+#include "tzdata.h"
 
 #include <chrono>
+#include <inttypes.h>
 
 namespace
 {
   using namespace rego;
   using namespace std::chrono;
 
-  Node now_ns(const Nodes&)
+  const std::size_t second_ns = 1000000000UL;
+  const std::size_t minute_ns = 60UL * second_ns;
+  const std::size_t hour_ns = 60UL * minute_ns;
+  const std::size_t day_ns = 24UL * hour_ns;
+
+  const std::map<std::string, std::string> predefined_layouts = {
+    {"Layout",
+     "01/02 03:04:05PM '06 -0700"}, // The reference time, in numerical order.
+    {"ANSIC", "Mon Jan _2 15:04:05 2006"},
+    {"UnixDate", "Mon Jan _2 15:04:05 MST 2006"},
+    {"RubyDate", "Mon Jan 02 15:04:05 -0700 2006"},
+    {"RFC822", "02 Jan 06 15:04 MST"},
+    {"RFC822Z", "02 Jan 06 15:04 -0700"}, // RFC822 with numeric zone
+    {"RFC850", "Monday, 02-Jan-06 15:04:05 MST"},
+    {"RFC1123", "Mon, 02 Jan 2006 15:04:05 MST"},
+    {"RFC1123Z",
+     "Mon, 02 Jan 2006 15:04:05 -0700"}, // RFC1123 with numeric zone
+    {"RFC3339", "2006-01-02T15:04:05Z07:00"},
+    {"RFC3339Nano", "2006-01-02T15:04:05.999999999Z07:00"},
+    {"Kitchen", "3:04PM"},
+    // Handy time stamps.
+    {"Stamp", "Jan _2 15:04:05"},
+    {"StampMilli", "Jan _2 15:04:05.000"},
+    {"StampMicro", "Jan _2 15:04:05.000000"},
+    {"StampNano", "Jan _2 15:04:05.000000000"},
+  };
+
+  const std::map<std::string, std::string> go_to_cpp = {
+    {"2006", "%Y"},    {"06", "%y"},        {"Jan", "%b"},
+    {"January", "%B"}, {"01", "%m"},        {"1", "%m"},
+    {"Mon", "%a"},     {"Monday", "%A"},    {"2", "%d"},
+    {"_2", "%d"},      {"02", "%d"},        {"__2", "%j"},
+    {"002", "%j"},     {"15", "%H"},        {"3", "%I"},
+    {"03", "%I"},      {"4", "%M"},         {"04", "%M"},
+    {"5", "%S"},       {"05", "%S"},        {"PM", "%p"},
+    {"PM", "%p"},      {"-0700", "%z"},     {"-07:00", "%Ez"},
+    {"-07", "%z"},     {"-070000", "%z"},   {"-07:00:00", "%Ez"},
+    {"Z0700", "%Z"},   {"Z07:00", "%Ez"},   {"Z07", "%Z"},
+    {"Z070000", "%Z"}, {"Z07:00:00", "%Ez"}};
+
+  enum class Resolution
+  {
+    Seconds,
+    Nano,
+    NanoZero,
+    Micro,
+    MicroZero,
+    Milli,
+    MilliZero
+  };
+
+  enum class InfoMode
+  {
+    Time,
+    TimeZone,
+    TimeFormat,
+    TimeZoneFormat
+  };
+
+  const std::map<std::string, Resolution> res_lookup = {
+    {".000000000", Resolution::Nano},
+    {".000000", Resolution::Micro},
+    {".000", Resolution::Milli},
+    {".999999999", Resolution::NanoZero},
+    {".999999", Resolution::MicroZero},
+    {".999", Resolution::MilliZero},
+    {"", Resolution::Seconds}};
+
+  struct TimeInfo
+  {
+    InfoMode mode;
+    nanoseconds ns;
+    std::string time_zone;
+    std::string format;
+    Resolution resolution;
+    size_t fraction_start;
+    Node error;
+  };
+
+  std::optional<nanoseconds> get_timestamp(Node x)
+  {
+    BigInt x_int = get_int(x);
+    if (x_int > std::numeric_limits<nanoseconds::rep>::max())
+    {
+      return std::nullopt;
+    }
+
+    return nanoseconds(x_int.to_int());
+  }
+
+  std::pair<std::string, std::pair<Resolution, size_t>> get_format(
+    const std::string& go_format_or_layout)
+  {
+    std::string go_format = go_format_or_layout;
+    if (contains(predefined_layouts, go_format))
+    {
+      go_format = predefined_layouts.at(go_format);
+    }
+
+    Resolution res = Resolution::Seconds;
+    size_t res_i = std::string::npos;
+    std::stringstream cpp_format;
+    size_t i = 0;
+    while (i < go_format.size())
+    {
+      bool found = false;
+      size_t max_len = std::min<size_t>(10, go_format.size() - i);
+      for (size_t len = max_len; len > 0; --len)
+      {
+        auto query = go_format.substr(i, len);
+        auto maybe_res = res_lookup.find(query);
+        if (maybe_res != res_lookup.end())
+        {
+          res_i = i;
+          res = maybe_res->second;
+          i += len;
+          found = true;
+          break;
+        }
+
+        auto maybe_cpp = go_to_cpp.find(query);
+        if (maybe_cpp != go_to_cpp.end())
+        {
+          cpp_format << maybe_cpp->second;
+          i += len;
+          found = true;
+          break;
+        }
+      }
+      if (!found)
+      {
+        cpp_format << go_format[i];
+        i++;
+      }
+    }
+
+    return {cpp_format.str(), {res, res_i}};
+  }
+
+  TimeInfo process_arg(
+    const Nodes& args, const std::string& func, std::size_t index)
+  {
+    Node x = unwrap_arg(args, UnwrapOpt(index).types({Int, Array}).func(func));
+    TimeInfo info = {
+      InfoMode::Time,
+      nanoseconds::zero(),
+      "",
+      "",
+      Resolution::Seconds,
+      std::string::npos,
+      nullptr};
+
+    if (x->type() == Error)
+    {
+      info.error = x;
+      return info;
+    }
+
+    if (x->type() == Int)
+    {
+      BigInt x_int = get_int(x);
+      auto maybe_ns = get_timestamp(x);
+      if (!maybe_ns.has_value())
+      {
+        info.error = err(x, "timestamp too big", EvalBuiltInError);
+        return info;
+      }
+
+      info.ns = maybe_ns.value();
+      return info;
+    }
+
+    if (x->size() < 2 || x->size() > 3)
+    {
+      info.error = err(x, "time.format: invalid argument", EvalTypeError);
+      return info;
+    }
+
+    auto maybe_int = unwrap(x->at(0), Int);
+    if (!maybe_int.success)
+    {
+      info.error =
+        err(x->at(0), "time.format: invalid timestamp", EvalTypeError);
+      return info;
+    }
+
+    auto maybe_ns = get_timestamp(maybe_int.node);
+    if (!maybe_ns.has_value())
+    {
+      info.error = err(maybe_int.node, "timestamp too big", EvalBuiltInError);
+      return info;
+    }
+
+    info.ns = maybe_ns.value();
+
+    auto maybe_timezone = unwrap(x->at(1), JSONString);
+    if (!maybe_timezone.success)
+    {
+      info.error =
+        err(x->at(1), "time.format: invalid time zone", EvalTypeError);
+      return info;
+    }
+
+    info.time_zone = get_string(maybe_timezone.node);
+
+    if (x->size() == 2)
+    {
+      if (!info.time_zone.empty())
+      {
+        info.mode = InfoMode::TimeZone;
+      }
+
+      return info;
+    }
+
+    auto maybe_format = unwrap(x->at(2), JSONString);
+    if (!maybe_format.success)
+    {
+      info.error =
+        err(x->at(2), "time.format: invalid format string", EvalTypeError);
+      return info;
+    }
+
+    auto [format, res] = get_format(get_string(maybe_format.node));
+    info.format = format;
+    info.resolution = res.first;
+    info.fraction_start = res.second;
+    if (info.time_zone.empty())
+    {
+      info.mode = InfoMode::TimeFormat;
+    }
+    else
+    {
+      info.mode = InfoMode::TimeZoneFormat;
+    }
+
+    return info;
+  }
+
+  Node time_string(const TimeInfo& info)
+  {
+    time_point<system_clock, nanoseconds> tp(info.ns);
+    auto time_s = date::format("%FT%TZ", tp);
+    return JSONString ^ time_s;
+  }
+
+  Node timezone_string(const TimeInfo& info)
+  {
+    auto zone = date::locate_zone(info.time_zone);
+    date::zoned_time<nanoseconds> zt_ns(
+      zone, date::sys_time<nanoseconds>(info.ns));
+    auto time_s = date::format("%FT%T%Ez", zt_ns);
+    return JSONString ^ time_s;
+  }
+
+  std::string insert_fraction(const std::string& time_s, const TimeInfo& info)
+  {
+    const int fraction_len = 32;
+    char fraction[fraction_len];
+    switch (info.resolution)
+    {
+      case Resolution::Nano:
+        snprintf(fraction, fraction_len, ".%09" PRId64, info.ns.count());
+        break;
+
+      case Resolution::NanoZero:
+        snprintf(fraction, fraction_len, ".%09" PRId64, info.ns.count());
+        break;
+
+      case Resolution::Micro:
+        snprintf(fraction, fraction_len, ".%06" PRId64, info.ns.count() / 1000);
+        break;
+
+      case Resolution::MicroZero:
+        snprintf(fraction, fraction_len, ".%06" PRId64, info.ns.count() / 1000);
+        break;
+
+      case Resolution::Milli:
+        snprintf(
+          fraction, fraction_len, ".%03" PRId64, info.ns.count() / 1000000);
+        break;
+
+      case Resolution::MilliZero:
+        snprintf(
+          fraction, fraction_len, ".%03" PRId64, info.ns.count() / 1000000);
+        break;
+
+      default:
+        // do nothing
+        break;
+    }
+
+    return time_s.substr(0, info.fraction_start) + fraction +
+      time_s.substr(info.fraction_start);
+  }
+
+  Node timeformat_string(const TimeInfo& info)
+  {
+    time_point<system_clock, nanoseconds> tp(info.ns);
+    auto time_s = date::format(info.format, tp);
+    if (info.resolution != Resolution::Seconds)
+    {
+      time_s = insert_fraction(time_s, info);
+    }
+
+    return JSONString ^ time_s;
+  }
+
+  Node timezoneformat_string(const TimeInfo& info)
+  {
+    auto zone = date::locate_zone(info.time_zone);
+    seconds s(floor<seconds>(info.ns));
+    date::zoned_time<seconds> zt(zone, date::sys_time<seconds>(s));
+    auto time_s = date::format(info.format, zt);
+
+    if (info.resolution != Resolution::Seconds)
+    {
+      time_s = insert_fraction(time_s, info);
+    }
+
+    return JSONString ^ time_s;
+  }
+
+  Node info_string(const TimeInfo& info)
+  {
+    switch (info.mode)
+    {
+      case InfoMode::Time:
+        return time_string(info);
+      case InfoMode::TimeZone:
+        return timezone_string(info);
+      case InfoMode::TimeFormat:
+        return timeformat_string(info);
+      case InfoMode::TimeZoneFormat:
+        return timezoneformat_string(info);
+      default:
+        throw std::runtime_error("Unsupported TimeInfo mode");
+    }
+  }
+
+  Node add_date(const Nodes& args)
+  {
+    Node ns_node =
+      unwrap_arg(args, UnwrapOpt(0).type(Int).func("time.add_date"));
+    if (ns_node->type() == Error)
+    {
+      return ns_node;
+    }
+
+    Node years_node =
+      unwrap_arg(args, UnwrapOpt(1).type(Int).func("time.add_date"));
+    if (years_node->type() == Error)
+    {
+      return years_node;
+    }
+
+    Node months_node =
+      unwrap_arg(args, UnwrapOpt(2).type(Int).func("time.add_date"));
+
+    if (months_node->type() == Error)
+    {
+      return months_node;
+    }
+
+    Node days_node =
+      unwrap_arg(args, UnwrapOpt(3).type(Int).func("time.add_date"));
+    if (days_node->type() == Error)
+    {
+      return days_node;
+    }
+
+    nanoseconds ns(get_int(ns_node).to_int());
+    date::days days_since_epoch = floor<date::days>(ns);
+    date::year_month_day ymd{date::sys_days(days_since_epoch)};
+    ymd += date::years(static_cast<int>(get_int(years_node).to_int()));
+    ymd += date::months(static_cast<unsigned>(get_int(months_node).to_size()));
+    std::int64_t days = date::sys_days(ymd).time_since_epoch().count();
+    days += get_int(days_node).to_int();
+
+    ns -= days_since_epoch;
+    BigInt time = days * BigInt(day_ns) + ns.count();
+
+    if (
+      time < std::numeric_limits<std::int64_t>::min() ||
+      time > std::numeric_limits<std::int64_t>::max())
+    {
+      return err(ns_node, "time outside of valid range", EvalBuiltInError);
+    }
+
+    return Resolver::term(time);
+  }
+
+  Node clock_(const Nodes& args)
+  {
+    TimeInfo info = process_arg(args, "time.clock", 0);
+    if (info.error != nullptr)
+    {
+      return info.error;
+    }
+
+    date::hh_mm_ss<nanoseconds> time;
+    if (info.mode == InfoMode::Time)
+    {
+      auto tp = date::sys_time<nanoseconds>(info.ns);
+      auto dp = floor<date::days>(tp);
+      time = date::hh_mm_ss<nanoseconds>(tp - dp);
+    }
+    else
+    {
+      auto zone = date::locate_zone(info.time_zone);
+      date::zoned_time<nanoseconds> zt_ns(
+        zone, date::sys_time<nanoseconds>(info.ns));
+      auto tp = zt_ns.get_local_time();
+      auto dp = floor<date::days>(tp);
+      time = date::hh_mm_ss<nanoseconds>(tp - dp);
+    }
+
+    std::int64_t hours_int = time.hours().count();
+    std::int64_t minutes_int = time.minutes().count();
+    std::int64_t seconds_int = time.seconds().count();
+    return Array << Resolver::term(BigInt(hours_int))
+                 << Resolver::term(BigInt(minutes_int))
+                 << Resolver::term(BigInt(seconds_int));
+  }
+
+  Node date_(const Nodes& args)
+  {
+    TimeInfo info = process_arg(args, "time.date", 0);
+    if (info.error != nullptr)
+    {
+      return info.error;
+    }
+
+    date::year_month_day ymd;
+    if (info.mode == InfoMode::Time)
+    {
+      time_point<system_clock, nanoseconds> tp(info.ns);
+      ymd = {date::floor<date::days>(tp)};
+    }
+    else
+    {
+      auto zone = date::locate_zone(info.time_zone);
+      date::zoned_time<nanoseconds> zt_ns(
+        zone, date::sys_time<nanoseconds>(info.ns));
+      auto ld = date::floor<date::days>(zt_ns.get_local_time());
+      ymd = date::year_month_day(ld);
+    }
+
+    std::int64_t year = int(ymd.year());
+    std::int64_t month = unsigned(ymd.month());
+    std::int64_t day = unsigned(ymd.day());
+    return Array << Resolver::term(BigInt(year))
+                 << Resolver::term(BigInt(month))
+                 << Resolver::term(BigInt(day));
+  }
+
+  Node diff(const Nodes& args)
+  {
+    TimeInfo info1 = process_arg(args, "time.diff", 0);
+    if (info1.error != nullptr)
+    {
+      return info1.error;
+    }
+
+    TimeInfo info2 = process_arg(args, "time.diff", 1);
+    if (info2.error != nullptr)
+    {
+      return info2.error;
+    }
+
+    date::sys_time<nanoseconds> tp1;
+    date::sys_time<nanoseconds> tp2;
+
+    if (info1.mode == InfoMode::Time)
+    {
+      tp1 = date::sys_time<nanoseconds>(info1.ns);
+    }
+    else
+    {
+      auto zone = date::locate_zone(info1.time_zone);
+      date::zoned_time<nanoseconds> zt_ns(
+        zone, date::sys_time<nanoseconds>(info1.ns));
+      tp1 = zt_ns.get_sys_time();
+    }
+
+    if (info2.mode == InfoMode::Time)
+    {
+      tp2 = date::sys_time<nanoseconds>(info2.ns);
+    }
+    else
+    {
+      auto zone = date::locate_zone(info2.time_zone);
+      date::zoned_time<nanoseconds> zt_ns(
+        zone, date::sys_time<nanoseconds>(info2.ns));
+      tp2 = zt_ns.get_sys_time();
+    }
+
+    seconds diff_s = duration_cast<seconds>(tp1 > tp2 ? tp1 - tp2 : tp2 - tp1);
+    date::years diff_years = floor<date::years>(diff_s);
+    diff_s -= seconds(diff_years.count() * 365 * 24 * 60 * 60);
+    date::months diff_months = floor<date::months>(diff_s);
+    diff_s -= diff_months;
+    date::days diff_days = floor<date::days>(diff_s);
+    diff_s -= diff_days;
+    hours diff_hours = floor<hours>(diff_s);
+    diff_s -= diff_hours;
+    minutes diff_minutes = floor<minutes>(diff_s);
+    diff_s -= diff_minutes;
+
+    std::int64_t years_int = diff_years.count();
+    std::int64_t months_int = diff_months.count();
+    std::int64_t days_int = diff_days.count();
+    std::int64_t hours_int = diff_hours.count();
+    std::int64_t minutes_int = diff_minutes.count();
+    std::int64_t seconds_int = diff_s.count();
+    return Array << Resolver::term(BigInt(years_int))
+                 << Resolver::term(BigInt(months_int))
+                 << Resolver::term(BigInt(days_int))
+                 << Resolver::term(BigInt(hours_int))
+                 << Resolver::term(BigInt(minutes_int))
+                 << Resolver::term(BigInt(seconds_int));
+  }
+
+  Node format(const Nodes& args)
+  {
+    TimeInfo info = process_arg(args, "time.format", 0);
+    if (info.error != nullptr)
+    {
+      return info.error;
+    }
+
+    return info_string(info);
+  }
+
+  const std::map<std::string, double> duration_units = {
+    {"ns", 1},
+    {"us", 1000},
+    {"µs", 1000},
+    {"ms", 1000000},
+    {"s", double(second_ns)},
+    {"m", double(minute_ns)},
+    {"h", double(hour_ns)}};
+
+  nanoseconds do_parse_duration(const std::string& duration)
   {
-    auto now = high_resolution_clock::now().time_since_epoch();
-    auto now_ns = duration_cast<nanoseconds>(now).count();
+    const char* duration_re =
+      R"((-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][-+]?[0-9]+)?)((?:ns|us|µs|ms|s|m|h)))";
+    const RE2 re(duration_re);
+    assert(re.ok());
+
+    std::string number;
+    std::string unit;
+    std::int64_t ns = 0;
+    std::size_t start = 0;
+    while (start < duration.size())
+    {
+      re2::StringPiece input(duration.c_str() + start, duration.size() - start);
+      if (RE2::PartialMatch(input, re, &number, &unit))
+      {
+        double number_d = std::stod(number);
+        double unit_ns = duration_units.at(unit);
+        ns += std::int64_t(number_d * unit_ns);
+        start += number.size() + unit.size();
+      }
+      else
+      {
+        break;
+      }
+    }
 
-    return Int ^ std::to_string(now_ns);
+    return nanoseconds(ns);
   }
+
+  Node parse_duration_ns(const Nodes& args)
+  {
+    Node duration = unwrap_arg(
+      args, UnwrapOpt(0).type(JSONString).func("time.parse_duration_ns"));
+    if (duration->type() == Error)
+    {
+      return duration;
+    }
+
+    std::int64_t ns = do_parse_duration(get_string(duration)).count();
+
+    if (ns != 0)
+    {
+      return Int ^ std::to_string(ns);
+    }
+
+    return Undefined;
+  }
+
+  Node do_parse(const std::string& layout, const Node& value)
+  {
+    std::string value_str = get_string(value);
+    auto [format, _] = get_format(layout);
+    std::istringstream is(value_str);
+    date::fields<nanoseconds> info;
+    minutes offset(0);
+    is >> date::parse(format, info, offset);
+
+    std::int64_t tod_ns = 0;
+    if (info.has_tod)
+    {
+      tod_ns = info.tod.to_duration().count();
+    }
+
+    std::int64_t offset_ns = offset.count() * minute_ns;
+
+    std::int64_t days = date::local_days(info.ymd).time_since_epoch().count();
+    BigInt ns = (days * BigInt(day_ns)) - offset_ns + tod_ns;
+
+    if (
+      ns < std::numeric_limits<std::int64_t>::min() ||
+      ns > std::numeric_limits<std::int64_t>::max())
+    {
+      return err(value, "time outside of valid range", EvalBuiltInError);
+    }
+
+    return Resolver::term(ns);
+  }
+
+  Node parse_ns(const Nodes& args)
+  {
+    Node layout =
+      unwrap_arg(args, UnwrapOpt(0).type(JSONString).func("time.parse_ns"));
+    if (layout->type() == Error)
+    {
+      return layout;
+    }
+
+    Node value =
+      unwrap_arg(args, UnwrapOpt(1).type(JSONString).func("time.parse_ns"));
+    if (value->type() == Error)
+    {
+      return value;
+    }
+
+    std::string layout_str = get_string(layout);
+    return do_parse(layout_str, value);
+  }
+
+  Node parse_rfc3339_ns(const Nodes& args)
+  {
+    Node value = unwrap_arg(
+      args, UnwrapOpt(0).type(JSONString).func("time.parse_rfc3339_ns"));
+    if (value->type() == Error)
+    {
+      return value;
+    }
+
+    return do_parse("RFC3339", value);
+  }
+
+  Node weekday(const Nodes& args)
+  {
+    TimeInfo info = process_arg(args, "time.weekday", 0);
+    if (info.error != nullptr)
+    {
+      return info.error;
+    }
+
+    info.mode = InfoMode::TimeZoneFormat;
+    info.format = "%A";
+    if (info.time_zone.empty())
+    {
+      info.time_zone = "UTC";
+    }
+
+    return info_string(info);
+  }
+
+  struct NowNS : public BuiltInDef
+  {
+    Node cached;
+
+    NowNS() :
+      BuiltInDef(
+        Location("time.now_ns"), 0, [this](const Nodes&) { return call(); })
+    {}
+
+    Node call()
+    {
+      if (cached != nullptr)
+      {
+        return cached->clone();
+      }
+
+      auto now = high_resolution_clock::now().time_since_epoch();
+      auto now_ns = duration_cast<nanoseconds>(now).count();
+      cached = Int ^ std::to_string(now_ns);
+      return cached;
+    }
+
+    void clear() override
+    {
+      cached = nullptr;
+    }
+  };
 }
 
 namespace rego
@@ -22,7 +720,23 @@ namespace rego
   {
     std::vector<BuiltIn> time()
     {
-      return {BuiltInDef::create(Location("time.now_ns"), 0, now_ns)};
+#ifdef REGOCPP_USE_MANUAL_TZDATA
+      date::set_install(REGOCPP_TZDATA_PATH);
+#endif
+
+      return {
+        BuiltInDef::create(Location("time.add_date"), 4, add_date),
+        BuiltInDef::create(Location("time.clock"), 1, clock_),
+        BuiltInDef::create(Location("time.date"), 1, date_),
+        BuiltInDef::create(Location("time.diff"), 2, diff),
+        BuiltInDef::create(Location("time.format"), 1, format),
+        std::make_shared<NowNS>(),
+        BuiltInDef::create(
+          Location("time.parse_duration_ns"), 1, parse_duration_ns),
+        BuiltInDef::create(Location("time.parse_ns"), 2, parse_ns),
+        BuiltInDef::create(
+          Location("time.parse_rfc3339_ns"), 1, parse_rfc3339_ns),
+        BuiltInDef::create(Location("time.weekday"), 1, ::weekday)};
     }
   }
 }
\ No newline at end of file
diff --git a/src/builtins/tzdata.h.in b/src/builtins/tzdata.h.in
new file mode 100644
index 00000000..8ef65e23
--- /dev/null
+++ b/src/builtins/tzdata.h.in
@@ -0,0 +1,10 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+#ifndef _REGO_BUILTINS_TZDATA_H_
+#define _REGO_BUILTINS_TZDATA_H_
+
+#cmakedefine  REGOCPP_TZDATA_PATH "@REGOCPP_TZDATA_PATH@/tzdata"
+#cmakedefine  REGOCPP_USE_MANUAL_TZDATA
+
+#endif
diff --git a/src/builtins/uuid.cc b/src/builtins/uuid.cc
new file mode 100644
index 00000000..75cb6b6d
--- /dev/null
+++ b/src/builtins/uuid.cc
@@ -0,0 +1,430 @@
+#include "builtins.h"
+
+namespace
+{
+  using namespace rego;
+
+  const std::int64_t lillian_15821015 = 2299160;
+  const std::int64_t unix_19700101 = 2440587;
+  const std::int64_t epoch = unix_19700101 - lillian_15821015;
+  const std::int64_t g1582 = epoch * 86400;
+  const std::int64_t g1582ns100 = g1582 * 10000000;
+  using uuid = std::array<std::uint8_t, 16>;
+
+  enum class uuid_variant
+  {
+    ncs,
+    rfc,
+    microsoft,
+    reserved
+  };
+
+  uuid_variant uuid_variant(const uuid& id)
+  {
+    if ((id[8] & 0x80) == 0x00)
+      return uuid_variant::ncs;
+
+    if ((id[8] & 0xC0) == 0x80)
+      return uuid_variant::rfc;
+
+    if ((id[8] & 0xE0) == 0xC0)
+      return uuid_variant::microsoft;
+
+    return uuid_variant::reserved;
+  }
+
+  bool is_hex(char c)
+  {
+    switch (c)
+    {
+      case '0':
+      case '1':
+      case '2':
+      case '3':
+      case '4':
+      case '5':
+      case '6':
+      case '7':
+      case '8':
+      case '9':
+      case 'a':
+      case 'A':
+      case 'b':
+      case 'B':
+      case 'c':
+      case 'C':
+      case 'd':
+      case 'D':
+      case 'e':
+      case 'E':
+      case 'f':
+      case 'F':
+        return true;
+
+      default:
+        return false;
+    }
+  }
+
+  std::uint8_t hex_to_byte(char c)
+  {
+    switch (c)
+    {
+      case '0':
+        return 0;
+
+      case '1':
+        return 1;
+
+      case '2':
+        return 2;
+
+      case '3':
+        return 3;
+
+      case '4':
+        return 4;
+
+      case '5':
+        return 5;
+
+      case '6':
+        return 6;
+
+      case '7':
+        return 7;
+
+      case '8':
+        return 8;
+
+      case '9':
+        return 9;
+
+      case 'a':
+      case 'A':
+        return 10;
+
+      case 'b':
+      case 'B':
+        return 11;
+
+      case 'c':
+      case 'C':
+        return 12;
+
+      case 'd':
+      case 'D':
+        return 13;
+
+      case 'e':
+      case 'E':
+        return 14;
+
+      case 'f':
+      case 'F':
+        return 15;
+    }
+
+    throw std::runtime_error("invalid hex character: " + std::to_string(c));
+  }
+
+  std::optional<uuid> uuid_from_string(const std::string& str)
+  {
+    bool firstDigit = true;
+    size_t hasBraces = 0;
+    size_t index = 0;
+
+    uuid id{{0}};
+
+    if (str.empty())
+      return std::nullopt;
+
+    if (str.front() == '{')
+      hasBraces = 1;
+
+    if (hasBraces && str.back() != '}')
+      return std::nullopt;
+
+    for (size_t i = hasBraces; i < str.size() - hasBraces; ++i)
+    {
+      if (str[i] == '-')
+        continue;
+
+      if (index >= 16 || !is_hex(str[i]))
+      {
+        return std::nullopt;
+      }
+
+      if (firstDigit)
+      {
+        id[index] = hex_to_byte(str[i]) << 4;
+        firstDigit = false;
+      }
+      else
+      {
+        id[index] = static_cast<uint8_t>(id[index] | hex_to_byte(str[i]));
+        index++;
+        firstDigit = true;
+      }
+    }
+
+    if (index < 16)
+    {
+      return std::nullopt;
+    }
+
+    return id;
+  }
+
+  std::string uuid_to_string(const uuid& id)
+  {
+    std::stringstream ss;
+    ss << std::hex << std::setfill('0');
+    for (int i = 0; i < 16; i++)
+    {
+      ss << std::setw(2) << int(id[i]);
+      if (i == 3 || i == 5 || i == 7 || i == 9)
+      {
+        ss << '-';
+      }
+    }
+    return ss.str();
+  }
+
+  uuid uuid_random(std::mt19937& generator)
+  {
+    std::uniform_int_distribution<uint32_t> distribution;
+    uuid id;
+    for (int i = 0; i < 16; i += 4)
+      *reinterpret_cast<uint32_t*>(id.data() + i) = distribution(generator);
+
+    // variant must be 10xxxxxx
+    id[8] &= 0xBF;
+    id[8] |= 0x80;
+
+    // version must be 0100xxxx
+    id[6] &= 0x4F;
+    id[6] |= 0x40;
+
+    return id;
+  }
+
+  BigInt get_time(const uuid& id)
+  {
+    std::int64_t time_low = (std::int64_t(id[0]) << 24) | (int(id[1]) << 16) |
+      (int(id[2]) << 8) | int(id[3]);
+    std::int64_t time_mid = (int(id[4]) << 8) | int(id[5]);
+    std::int64_t time_hi = ((int(id[6]) << 8) | int(id[7])) & 0x0FFF;
+    std::int64_t time = (time_hi << 48) | (time_mid << 32) | time_low;
+    std::int64_t time_ns100 = time - g1582ns100;
+    return time_ns100 * 100;
+  }
+
+  BigInt get_clock_sequence(const uuid& id)
+  {
+    std::int64_t clock_seq = (int(id[8]) << 8) | int(id[9]);
+    return clock_seq & 0x3FFF;
+  }
+
+  std::string get_node_id(const uuid& id)
+  {
+    std::stringstream ss;
+    ss << std::hex << std::setfill('0');
+    for (int i = 10; i < 16; i++)
+    {
+      ss << std::setw(2) << int(id[i]);
+      if (i < 15)
+      {
+        ss << '-';
+      }
+    }
+    return ss.str();
+  }
+
+  std::string get_mac_vars(const uuid& id)
+  {
+    switch (int(id[10]) & 0b11)
+    {
+      case 0xb11:
+        return "local:multicast";
+
+      case 0b10:
+        return "local:unicast";
+
+      case 0b01:
+        return "global:multicast";
+
+      default:
+        return "global:unicast";
+    }
+  }
+
+  BigInt get_id(const uuid& id)
+  {
+    return (std::int64_t(id[0]) << 24) | (int(id[1]) << 16) |
+      (int(id[2]) << 8) | int(id[3]);
+  }
+
+  std::string get_domain(const uuid& id)
+  {
+    auto d = int(id[9]);
+    switch (d)
+    {
+      case 0:
+        return "Person";
+
+      case 1:
+        return "Group";
+
+      case 2:
+        return "Org";
+
+      default:
+        return "Domain" + std::to_string(d);
+    }
+  }
+
+  Node parse(const Nodes& args)
+  {
+    Node uuid_node =
+      unwrap_arg(args, UnwrapOpt(0).func("uuid.parse").type(JSONString));
+    if (uuid_node->type() == Error)
+    {
+      return uuid_node;
+    }
+
+    std::string uuid_str = get_string(uuid_node);
+    if (starts_with(uuid_str, "urn:uuid:"))
+    {
+      uuid_str = uuid_str.substr(9);
+    }
+
+    std::optional<uuid> maybe_id = uuid_from_string(uuid_str);
+
+    if (maybe_id.has_value())
+    {
+      uuid id = maybe_id.value();
+      Node result = NodeDef::create(Object);
+      switch (uuid_variant(id))
+      {
+        case uuid_variant::ncs:
+          result
+            << (ObjectItem << Resolver::term("variant")
+                           << Resolver::term("ncs"));
+          break;
+        case uuid_variant::rfc:
+          result
+            << (ObjectItem << Resolver::term("variant")
+                           << Resolver::term("RFC4122"));
+          break;
+        case uuid_variant::microsoft:
+          result
+            << (ObjectItem << Resolver::term("variant")
+                           << Resolver::term("microsoft"));
+          break;
+        case uuid_variant::reserved:
+          result
+            << (ObjectItem << Resolver::term("variant")
+                           << Resolver::term("reserved"));
+          break;
+
+        default:
+          return err(uuid_node, "unknown uuid variant");
+      }
+
+      std::size_t version = static_cast<std::size_t>(id[6] >> 4);
+      result
+        << (ObjectItem << Resolver::term("version")
+                       << Resolver::term(BigInt(version)));
+
+      if (version == 1 || version == 2)
+      {
+        result
+          << (ObjectItem << Resolver::term("time")
+                         << Resolver::term(get_time(id)));
+        result
+          << (ObjectItem << Resolver::term("clocksequence")
+                         << Resolver::term(get_clock_sequence(id)));
+        result
+          << (ObjectItem << Resolver::term("nodeid")
+                         << Resolver::term(get_node_id(id)));
+        result
+          << (ObjectItem << Resolver::term("macvariables")
+                         << Resolver::term(get_mac_vars(id)));
+        if (version == 2)
+        {
+          result
+            << (ObjectItem << Resolver::term("id")
+                           << Resolver::term(get_id(id)));
+          result
+            << (ObjectItem << Resolver::term("domain")
+                           << Resolver::term(get_domain(id)));
+        }
+      }
+
+      return result;
+    }
+    else
+    {
+      return err(uuid_node, "invalid uuid");
+    }
+  }
+
+  struct UUIDRFC4122 : public BuiltInDef
+  {
+    std::random_device rd;
+    std::mt19937 generator;
+    std::map<std::string, Node> cache;
+
+    UUIDRFC4122() :
+      BuiltInDef(Location("uuid.rfc4122"), 1, [this](const Nodes& args) {
+        return call(args);
+      })
+    {
+      auto seed_data = std::array<int, std::mt19937::state_size>{};
+      std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
+      std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
+      generator = std::mt19937(seq);
+    }
+
+    Node call(const Nodes& args)
+    {
+      Node k =
+        unwrap_arg(args, UnwrapOpt(0).func("uuid.rfc4122").type(JSONString));
+      if (k->type() == Error)
+      {
+        return k;
+      }
+
+      std::string k_str = get_string(k);
+      if (contains(cache, k_str))
+      {
+        return cache[k_str]->clone();
+      }
+
+      std::string uuid_str = uuid_to_string(uuid_random(generator));
+      Node uuid_node = JSONString ^ uuid_str;
+      cache[k_str] = uuid_node;
+      return uuid_node;
+    }
+
+    void clear() override
+    {
+      cache.clear();
+    }
+  };
+}
+
+namespace rego
+{
+  namespace builtins
+  {
+    std::vector<BuiltIn> uuid()
+    {
+      return {
+        BuiltInDef::create(Location("uuid.parse"), 1, parse),
+        std::make_shared<UUIDRFC4122>(),
+      };
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/internal.hh b/src/internal.hh
index fa36ffe2..66cda6f9 100644
--- a/src/internal.hh
+++ b/src/internal.hh
@@ -103,6 +103,7 @@ namespace rego
     static Node reduce_termset(const Node& termset);
     static void insert_into_object(
       Node& object, const std::string& path, const Node& value);
+    static Nodes walk(Node x);
   };
 
   inline const auto NewLine = TokenDef("rego-newline");
diff --git a/src/interpreter.cc b/src/interpreter.cc
index 7e02b3c7..456031d9 100644
--- a/src/interpreter.cc
+++ b/src/interpreter.cc
@@ -241,6 +241,8 @@ namespace rego
       }
     }
 
+    m_builtins->clear();
+
     result = m_ast >> m_unify;
     if (!result.ok)
     {
diff --git a/src/resolver.cc b/src/resolver.cc
index a1e748a6..0a2c6976 100644
--- a/src/resolver.cc
+++ b/src/resolver.cc
@@ -1766,4 +1766,64 @@ namespace rego
         existing, ObjectItem << (Term << key) << (Term << value->clone()));
     }
   }
+
+  Nodes Resolver::walk(Node x)
+  {
+    Nodes nodes;
+    std::vector<std::pair<Nodes, Node>> stack;
+    stack.push_back({{}, x});
+    while (!stack.empty())
+    {
+      auto [path_nodes, current] = stack.back();
+      stack.pop_back();
+
+      Node path_array = NodeDef::create(Array);
+      for (auto& node : path_nodes)
+      {
+        path_array->push_back(to_term(node->clone()));
+      }
+
+      nodes.push_back(
+        Array << (Term << path_array) << to_term(current->clone()));
+
+      auto maybe_node =
+        unwrap(current, {Array, Set, DynamicSet, Object, DynamicObject});
+      if (!maybe_node.success)
+      {
+        continue;
+      }
+
+      current = maybe_node.node;
+      if (current == Array)
+      {
+        for (size_t i = 0; i < current->size(); i++)
+        {
+          Node index = term(BigInt(i));
+          Nodes path(path_nodes);
+          path.push_back(index);
+          stack.push_back({path, current->at(i)});
+        }
+      }
+      else if (current->in({Set, DynamicSet}))
+      {
+        for (auto child : *current)
+        {
+          Nodes path(path_nodes);
+          path.push_back(child);
+          stack.push_back({path, child});
+        }
+      }
+      else if (current->in({Object, DynamicObject}))
+      {
+        for (auto child : *current)
+        {
+          Nodes path(path_nodes);
+          path.push_back(child / Key);
+          stack.push_back({path, child / Val});
+        }
+      }
+    }
+
+    return nodes;
+  }
 }
\ No newline at end of file
diff --git a/src/unifier.cc b/src/unifier.cc
index 295b27db..7787ef3e 100644
--- a/src/unifier.cc
+++ b/src/unifier.cc
@@ -756,6 +756,11 @@ namespace rego
         Values arg_values = enumerate(var, args_node->front());
         values.insert(values.end(), arg_values.begin(), arg_values.end());
       }
+      else if (func_name == "walk")
+      {
+        Values arg_values = walk(var, args_node->front());
+        values.insert(values.end(), arg_values.begin(), arg_values.end());
+      }
       else if (func_name == "merge")
       {
         Values partials = resolve_var(args_node->front());
@@ -1296,6 +1301,23 @@ namespace rego
     return items;
   }
 
+  Values UnifierDef::walk(const Location& var, const Node& root_var)
+  {
+    Values values;
+
+    Values root_values = resolve_var(root_var);
+    for (auto& value : root_values)
+    {
+      Nodes path_tuples = Resolver::walk(value->node());
+      for (Node tuple : path_tuples)
+      {
+        values.push_back(ValueDef::create(var, tuple));
+      }
+    }
+
+    return values;
+  }
+
   std::string UnifierDef::str() const
   {
     std::ostringstream buf;
diff --git a/src/unify.hh b/src/unify.hh
index a3284c72..e9ebacae 100644
--- a/src/unify.hh
+++ b/src/unify.hh
@@ -38,7 +38,9 @@ namespace rego
   inline const auto LiteralEnum = TokenDef("rego-literalenum");
   inline const auto LiteralInit = TokenDef("rego-literalinit");
   inline const auto LiteralNot = TokenDef("rego-literalnot");
+  inline const auto LiteralWalk = TokenDef("rego-literalwalk");
   inline const auto Enumerate = TokenDef("rego-enumerate");
+  inline const auto Walk = TokenDef("rego-walk");
   inline const auto Merge = TokenDef("rego-merge");
   inline const auto UnifyBody = TokenDef("rego-unifybody");
   inline const auto Arg = TokenDef("rego-arg");
@@ -54,6 +56,7 @@ namespace rego
   inline const auto UnifyExprWith = TokenDef("rego-unifyexprwith");
   inline const auto UnifyExprEnum = TokenDef("rego-unifyexprenum");
   inline const auto UnifyExprCompr = TokenDef("rego-unifyexprcompr");
+  inline const auto UnifyExprWalk = TokenDef("rego-unifyexprwalk");
   inline const auto Compr = TokenDef("rego-compr");
   inline const auto SkipSeq = TokenDef("rego-skipseq");
   inline const auto Skip = TokenDef("rego-skip", flag::lookup);
@@ -142,10 +145,11 @@ namespace rego
     | (RuleFunc <<= Var * RuleArgs * (Body >>= UnifyBody | Empty) * (Val >>= UnifyBody | Term) * Version * (Idx >>= Int))[Var]
     | (RuleSet <<= Var * (Body >>= UnifyBody | Empty) * (Val >>= Expr | Term) * Version)[Var]
     | (RuleObj <<= Var * (Body >>= UnifyBody | Empty) * (Key >>= Expr | Term) * (Val >>= Expr | Term) * (IsVarRef >>= True | False) * Version)[Var]
-    | (UnifyBody <<= (Local | Literal | LiteralWith | LiteralEnum)++[1])
+    | (UnifyBody <<= (Local | Literal | LiteralWith | LiteralEnum | LiteralWalk)++[1])
     | (Local <<= Var * (Val >>= Undefined))[Var]
     | (Literal <<= Expr | NotExpr | SomeDecl)
     | (LiteralEnum <<= (Item >>= Var) * (ItemSeq >>= Expr))
+    | (LiteralWalk <<= (Item >>= Var) * (ItemSeq >>= Expr))
     | (LiteralWith <<= UnifyBody * WithSeq)
     | (With <<= RuleRef * Expr)
     | (ExprCall <<= RuleRef * ExprSeq)
@@ -195,7 +199,9 @@ namespace rego
   // clang-format off
   inline const auto wf_pass_explicit_enums = 
     wf_pass_constants
-    | (LiteralEnum <<= (Item >>= Var) * (ItemSeq >>= Var) * UnifyBody);
+    | (LiteralEnum <<= (Item >>= Var) * (ItemSeq >>= Var) * UnifyBody)
+    | (LiteralWalk <<= (Item >>= Var) * (ItemSeq >>= Var) * UnifyBody)
+    ;
   // clang-format on
 
   inline const auto wf_pass_locals = wf_pass_explicit_enums;
@@ -254,7 +260,7 @@ namespace rego
     | (BinInfix <<= Expr * (Op >>= And | Or) * Expr)
     | (BoolInfix <<= Expr * (Op >>= Equals | NotEquals | LessThan | LessThanOrEquals | GreaterThan | GreaterThanOrEquals) * Expr)
     | (Expr <<= wf_infix_exprs)
-    | (UnifyBody <<= (Local | Literal | LiteralWith | LiteralEnum | LiteralNot)++[1])
+    | (UnifyBody <<= (Local | Literal | LiteralWith | LiteralEnum | LiteralWalk | LiteralNot)++[1])
     | (LiteralNot <<= UnifyBody)
     | (Literal <<= Expr)
     ;
@@ -290,7 +296,7 @@ namespace rego
   // clang-format off
   inline const auto wf_pass_init =
     wf_pass_simple_refs
-    | (UnifyBody <<= (Local | Literal | LiteralWith | LiteralEnum | LiteralNot | LiteralInit)++[1])
+    | (UnifyBody <<= (Local | Literal | LiteralWith | LiteralEnum | LiteralWalk | LiteralNot | LiteralInit)++[1])
     | (LiteralInit <<= (Lhs >>= VarSeq) * (Rhs >>= VarSeq) * AssignInfix)
     | (VarSeq <<= Var++)
     ;
@@ -308,10 +314,11 @@ namespace rego
     | (Module <<= (Import | RuleComp | RuleFunc | RuleSet | RuleObj | Submodule)++)
     | (UnifyExpr <<= Var * (Val >>= Expr))
     | (Expr <<= wf_rulebody_exprs)
-    | (UnifyBody <<= (Local | UnifyExpr | UnifyExprWith | UnifyExprCompr | UnifyExprEnum | UnifyExprNot)++[1])
+    | (UnifyBody <<= (Local | UnifyExpr | UnifyExprWith | UnifyExprCompr | UnifyExprEnum | UnifyExprWalk | UnifyExprNot)++[1])
     | (UnifyExprWith <<= UnifyBody * WithSeq)
     | (UnifyExprCompr <<= Var * (Val >>= ArrayCompr | SetCompr | ObjectCompr) * NestedBody)
     | (UnifyExprEnum <<= Var * (Item >>= Var) * (ItemSeq >>= Var) * UnifyBody)
+    | (UnifyExprWalk <<= Var * (Item >>= Var) * (ItemSeq >>= Var) * UnifyBody)
     | (UnifyExprNot <<= UnifyBody)
     | (ArrayCompr <<= Var)
     | (SetCompr <<= Var)
@@ -320,8 +327,8 @@ namespace rego
     ;
   // clang-format on
 
-  inline const auto wf_lift_to_rule_exprs =
-    wf_rulebody_exprs | Enumerate | ArrayCompr | SetCompr | ObjectCompr | Merge;
+  inline const auto wf_lift_to_rule_exprs = wf_rulebody_exprs | Enumerate |
+    Walk | ArrayCompr | SetCompr | ObjectCompr | Merge;
 
   // clang-format off
   inline const auto wf_pass_lift_to_rule =
@@ -330,6 +337,7 @@ namespace rego
     | (Expr <<= wf_lift_to_rule_exprs)
     | (Merge <<= Var)
     | (Enumerate <<= Expr)
+    | (Walk <<= Expr)
     ;
   // clang-format on
 
@@ -642,6 +650,7 @@ namespace rego
       const Location& var, const std::string& func_name, const Args& args);
     Values resolve_var(const Node& var, bool exclude_with = false);
     Values enumerate(const Location& var, const Node& container);
+    Values walk(const Location& var, const Node& container);
     Values resolve_skip(const Node& skip);
     Values check_with(const Node& var, bool bypass_recurse_check = false);
     Values apply_access(const Location& var, const Values& args);
diff --git a/src/unify/compr.cc b/src/unify/compr.cc
index ea73c49c..70de4372 100644
--- a/src/unify/compr.cc
+++ b/src/unify/compr.cc
@@ -167,8 +167,8 @@ namespace rego
             // statement in a UnifyBody, we can find them by checking
             // the final statement.
             Node innermost = _(NestedBody) / Val;
-            while (innermost->back()->type() == LiteralEnum ||
-                   innermost->back()->type() == LiteralWith)
+            while (
+              innermost->back()->in({LiteralEnum, LiteralWalk, LiteralWith}))
             {
               innermost = innermost->back() / UnifyBody;
             }
@@ -188,8 +188,8 @@ namespace rego
             Location out = _.fresh({"out"});
             Node innermost = _(NestedBody) / Val;
             // see comment above
-            while (innermost->back()->type() == LiteralEnum ||
-                   innermost->back()->type() == LiteralWith)
+            while (
+              innermost->back()->in({LiteralEnum, LiteralWalk, LiteralWith}))
             {
               innermost = innermost->back() / UnifyBody;
             }
diff --git a/src/unify/enumerate.cc b/src/unify/enumerate.cc
index f8b151b8..ecb569d0 100644
--- a/src/unify/enumerate.cc
+++ b/src/unify/enumerate.cc
@@ -5,7 +5,7 @@ namespace
   using namespace rego;
 
   const auto inline LiteralToken = T(Literal) / T(LiteralWith) /
-    T(LiteralEnum) / T(LiteralInit) / T(LiteralNot) / T(Local);
+    T(LiteralEnum) / T(LiteralWalk) / T(LiteralInit) / T(LiteralNot) / T(Local);
 
   void find_all_refs_in(const Node& node, Location loc, Nodes& refs)
   {
@@ -51,7 +51,7 @@ namespace
     for (auto it = start; it != unifybody->end(); ++it)
     {
       Node node = *it;
-      if (node->type() == LiteralEnum)
+      if (node->in({LiteralEnum, LiteralWalk}))
       {
         return node;
       }
@@ -78,11 +78,11 @@ namespace rego
       dir::topdown,
       {
         In(UnifyBody) *
-            ((T(LiteralEnum) << (T(Var)[Item] * T(Expr)[ItemSeq])) *
+            ((T(LiteralEnum, LiteralWalk)[Op]
+              << (T(Var)[Item] * T(Expr)[ItemSeq])) *
              LiteralToken++[Tail] * End) >>
           [](Match& _) {
             ACTION();
-            auto temp = _.fresh({"enum"});
             auto itemseq = _.fresh({"itemseq"});
             // all statements under the LiteralEnum node must be moved to its
             // body, as they depend on the enumerated values.
@@ -91,14 +91,15 @@ namespace rego
             {
               body << (Literal << (Expr << (Term << (Scalar << True))));
             }
+            Token token = _(Op)->type();
             return Seq << (Local << (Var ^ itemseq) << Undefined)
                        << (Literal
                            << (Expr << expr_infix(
                                  Unify,
                                  RefTerm << (Var ^ itemseq),
                                  _(ItemSeq))))
-                       << (LiteralEnum << _(Item)->clone() << (Var ^ itemseq)
-                                       << body);
+                       << (token << _(Item)->clone() << (Var ^ itemseq)
+                                 << body);
           },
       }};
   }
@@ -171,7 +172,6 @@ namespace rego
               return err(idx, "Invalid index for enumeration");
             }
 
-            auto temp = _.fresh({"enum"});
             auto item = _.fresh({"item"});
             return Seq
               << (Local << (Var ^ item) << Undefined)
@@ -240,7 +240,6 @@ namespace rego
               idx = Term << idx;
             }
 
-            auto temp = _.fresh({"enum"});
             auto item = _.fresh({"item"});
             return Seq
               << (Local << (Var ^ item) << Undefined)
@@ -299,7 +298,7 @@ namespace rego
         return 0;
       }
 
-      if (is_in(local, {LiteralEnum}))
+      if (is_in(local, {LiteralEnum, LiteralWalk}))
       {
         // should this local be defined here?
         Node unifybody = local->parent();
@@ -310,7 +309,7 @@ namespace rego
           // correct scope
           requires_move = true;
           Node literalenum = unifybody->parent();
-          if (literalenum->type() != LiteralEnum)
+          if (!literalenum->in({LiteralEnum, LiteralWalk}))
           {
             // we've popped out of the nested enums
             break;
diff --git a/src/unify/functions.cc b/src/unify/functions.cc
index 88c74fa2..7738f51f 100644
--- a/src/unify/functions.cc
+++ b/src/unify/functions.cc
@@ -273,6 +273,13 @@ namespace rego
                             << (ArgSeq << unwrap_node(_(Expr)));
           },
 
+        In(UnifyExpr, ArgSeq) * (T(Walk) << T(Expr)[Expr]) >>
+          [](Match& _) {
+            ACTION();
+            return Function << (JSONString ^ "walk")
+                            << (ArgSeq << unwrap_node(_(Expr)));
+          },
+
         In(UnifyExpr, ArgSeq) *
             (T(Membership)
              << ((T(ExprSeq) << (T(Expr)[Idx] * T(Expr)[Item] * End)) *
diff --git a/src/unify/init.cc b/src/unify/init.cc
index 5ae8ab3a..8f43ccd2 100644
--- a/src/unify/init.cc
+++ b/src/unify/init.cc
@@ -260,11 +260,11 @@ namespace
         stmts.push_back({i, {}, {}});
         logging::Trace() << "Local: " << loc.view();
       }
-      else if (stmt->type() == LiteralEnum)
+      else if (stmt->in({LiteralEnum, LiteralWalk}))
       {
-        // a literal enum initializes its item from the item sequence, which
-        // cannot be an init statement. The item variable is only
-        // used inside the enum, so we can safely ignore it.
+        // a literal enum/walk initializes its item from the item sequence,
+        // which cannot be an init statement. The item variable is only used
+        // inside the enum, so we can safely ignore it.
         Location item_loc = (stmt / Item)->location();
         locals.erase(item_loc);
         StmtSide rhs;
@@ -439,7 +439,7 @@ namespace
     // where appropriate, recurse with the updated locals
     for (Node stmt : ordered_stmts)
     {
-      if (stmt->in({LiteralEnum, LiteralWith, LiteralNot}))
+      if (stmt->in({LiteralEnum, LiteralWalk, LiteralWith, LiteralNot}))
       {
         find_init_stmts(stmt / UnifyBody, locals, outer_inits);
       }
diff --git a/src/unify/lift_to_rule.cc b/src/unify/lift_to_rule.cc
index d8a0627d..4a14d2d2 100644
--- a/src/unify/lift_to_rule.cc
+++ b/src/unify/lift_to_rule.cc
@@ -170,7 +170,7 @@ namespace rego
       dir::bottomup,
       {
         In(UnifyBody) *
-            ((T(UnifyExprEnum) * In(DataModule)++)
+            ((T(UnifyExprEnum, UnifyExprWalk)[Op] * In(DataModule)++)
              << (T(Var)[Var] * T(Var)[Item] * T(Var)[ItemSeq] *
                  T(UnifyBody)[UnifyBody])) >>
           [](Match& _) {
@@ -246,13 +246,14 @@ namespace rego
                               << (Expr << (Term << rulevalue)));
             }
 
+            Token func = _(Op)->type() == UnifyExprEnum ? Enumerate : Walk;
             Node result = Seq
               << (Lift << DataModule
                        << (RuleFunc << rulename << ruleargs << rulebody
                                     << rulevalue << version << (Int ^ "0")))
               << (UnifyExpr
                   << _(Item)
-                  << (Expr << (Enumerate << (Expr << (RefTerm << _(ItemSeq))))))
+                  << (Expr << (func << (Expr << (RefTerm << _(ItemSeq))))))
               << (UnifyExpr
                   << _(Var)
                   << (Expr << (ExprCall << rulename->clone() << exprseq)));
diff --git a/src/unify/rulebody.cc b/src/unify/rulebody.cc
index 88ea21ab..cfcfae5e 100644
--- a/src/unify/rulebody.cc
+++ b/src/unify/rulebody.cc
@@ -132,15 +132,17 @@ namespace rego
           },
 
         In(UnifyBody) *
-            (T(LiteralEnum)
+            (T(LiteralEnum, LiteralWalk)[Op]
              << (T(Var)[Lhs] * T(Var)[Rhs] * T(UnifyBody)[UnifyBody])) >>
           [](Match& _) {
             ACTION();
             Location value = _.fresh({in_query(_(Lhs)) ? "qvalue" : "value"});
+            Token token =
+              _(Op)->type() == LiteralEnum ? UnifyExprEnum : UnifyExprWalk;
             return Seq << (Lift << UnifyBody
                                 << (Local << (Var ^ value) << Undefined))
-                       << (UnifyExprEnum << (Var ^ value) << _(Lhs) << _(Rhs)
-                                         << _(UnifyBody));
+                       << (token << (Var ^ value) << _(Lhs) << _(Rhs)
+                                 << _(UnifyBody));
           },
 
         // LiteralInit nodes are handled separately. They designate what the
diff --git a/src/unify/symbols.cc b/src/unify/symbols.cc
index 421c8ccf..18cf5f31 100644
--- a/src/unify/symbols.cc
+++ b/src/unify/symbols.cc
@@ -763,6 +763,26 @@ namespace rego
                        << (Var ^ name);
           },
 
+        In(UnifyBody, LiteralWith) *
+            (T(Literal)
+             << (T(Expr)
+                 << (T(ExprCall)
+                     << ((T(RuleRef)
+                          << (T(Ref)
+                              << ((T(RefHead) << T(Var, "walk")) *
+                                  (T(RefArgSeq) << End)))) *
+                         (T(ExprSeq)
+                          << (T(Expr)[ItemSeq] * T(Expr)[Item] * End)))))) >>
+          [](Match& _) {
+            ACTION();
+            Location item = _.fresh({"item"});
+            return Seq << (Local << (Var ^ item) << Undefined)
+                       << (LiteralWalk << (Var ^ item) << _(ItemSeq))
+                       << (Literal
+                           << (Expr << expr_infix(
+                                 Unify, _(Item), (RefTerm << (Var ^ item)))));
+          },
+
         // errors
 
         In(Policy) * T(Rule)[Rule] >>
diff --git a/templates/tzdata/windowsZones.xml b/templates/tzdata/windowsZones.xml
new file mode 100644
index 00000000..687e5f32
--- /dev/null
+++ b/templates/tzdata/windowsZones.xml
@@ -0,0 +1,794 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE supplementalData SYSTEM "../../common/dtd/ldmlSupplemental.dtd">
+<!--
+Copyright © 1991-2013 Unicode, Inc.
+CLDR data files are interpreted according to the LDML specification (http://unicode.org/reports/tr35/)
+For terms of use, see http://www.unicode.org/copyright.html
+-->
+
+<supplementalData>
+	<version number="$Revision$"/>
+	<windowsZones>
+		<mapTimezones otherVersion="7e11800" typeVersion="2021a">
+
+			<!-- (UTC-12:00) International Date Line West -->
+			<mapZone other="Dateline Standard Time" territory="001" type="Etc/GMT+12"/>
+			<mapZone other="Dateline Standard Time" territory="ZZ" type="Etc/GMT+12"/>
+
+			<!-- (UTC-11:00) Coordinated Universal Time-11 -->
+			<mapZone other="UTC-11" territory="001" type="Etc/GMT+11"/>
+			<mapZone other="UTC-11" territory="AS" type="Pacific/Pago_Pago"/>
+			<mapZone other="UTC-11" territory="NU" type="Pacific/Niue"/>
+			<mapZone other="UTC-11" territory="UM" type="Pacific/Midway"/>
+			<mapZone other="UTC-11" territory="ZZ" type="Etc/GMT+11"/>
+
+			<!-- (UTC-10:00) Aleutian Islands -->
+			<mapZone other="Aleutian Standard Time" territory="001" type="America/Adak"/>
+			<mapZone other="Aleutian Standard Time" territory="US" type="America/Adak"/>
+
+			<!-- (UTC-10:00) Hawaii -->
+			<mapZone other="Hawaiian Standard Time" territory="001" type="Pacific/Honolulu"/>
+			<mapZone other="Hawaiian Standard Time" territory="CK" type="Pacific/Rarotonga"/>
+			<mapZone other="Hawaiian Standard Time" territory="PF" type="Pacific/Tahiti"/>
+			<mapZone other="Hawaiian Standard Time" territory="US" type="Pacific/Honolulu"/>
+			<mapZone other="Hawaiian Standard Time" territory="ZZ" type="Etc/GMT+10"/>
+
+			<!-- (UTC-09:30) Marquesas Islands -->
+			<mapZone other="Marquesas Standard Time" territory="001" type="Pacific/Marquesas"/>
+			<mapZone other="Marquesas Standard Time" territory="PF" type="Pacific/Marquesas"/>
+
+			<!-- (UTC-09:00) Alaska -->
+			<mapZone other="Alaskan Standard Time" territory="001" type="America/Anchorage"/>
+			<mapZone other="Alaskan Standard Time" territory="US" type="America/Anchorage America/Juneau America/Metlakatla America/Nome America/Sitka America/Yakutat"/>
+
+			<!-- (UTC-09:00) Coordinated Universal Time-09 -->
+			<mapZone other="UTC-09" territory="001" type="Etc/GMT+9"/>
+			<mapZone other="UTC-09" territory="PF" type="Pacific/Gambier"/>
+			<mapZone other="UTC-09" territory="ZZ" type="Etc/GMT+9"/>
+
+			<!-- (UTC-08:00) Baja California -->
+			<mapZone other="Pacific Standard Time (Mexico)" territory="001" type="America/Tijuana"/>
+			<mapZone other="Pacific Standard Time (Mexico)" territory="MX" type="America/Tijuana"/>
+
+			<!-- (UTC-08:00) Coordinated Universal Time-08 -->
+			<mapZone other="UTC-08" territory="001" type="Etc/GMT+8"/>
+			<mapZone other="UTC-08" territory="PN" type="Pacific/Pitcairn"/>
+			<mapZone other="UTC-08" territory="ZZ" type="Etc/GMT+8"/>
+
+			<!-- (UTC-08:00) Pacific Time (US & Canada) -->
+			<mapZone other="Pacific Standard Time" territory="001" type="America/Los_Angeles"/>
+			<mapZone other="Pacific Standard Time" territory="CA" type="America/Vancouver"/>
+			<mapZone other="Pacific Standard Time" territory="US" type="America/Los_Angeles"/>
+
+			<!-- (UTC-07:00) Arizona -->
+			<mapZone other="US Mountain Standard Time" territory="001" type="America/Phoenix"/>
+			<mapZone other="US Mountain Standard Time" territory="CA" type="America/Creston America/Dawson_Creek America/Fort_Nelson"/>
+			<mapZone other="US Mountain Standard Time" territory="MX" type="America/Hermosillo"/>
+			<mapZone other="US Mountain Standard Time" territory="US" type="America/Phoenix"/>
+			<mapZone other="US Mountain Standard Time" territory="ZZ" type="Etc/GMT+7"/>
+
+			<!-- (UTC-07:00) Chihuahua, La Paz, Mazatlan -->
+			<mapZone other="Mountain Standard Time (Mexico)" territory="001" type="America/Mazatlan"/>
+			<mapZone other="Mountain Standard Time (Mexico)" territory="MX" type="America/Mazatlan"/>
+
+			<!-- (UTC-07:00) Mountain Time (US & Canada) -->
+			<mapZone other="Mountain Standard Time" territory="001" type="America/Denver"/>
+			<mapZone other="Mountain Standard Time" territory="CA" type="America/Edmonton America/Cambridge_Bay America/Inuvik"/>
+			<mapZone other="Mountain Standard Time" territory="MX" type="America/Ciudad_Juarez"/>
+			<mapZone other="Mountain Standard Time" territory="US" type="America/Denver America/Boise"/>
+
+			<!-- (UTC-07:00) Yukon -->
+			<mapZone other="Yukon Standard Time" territory="001" type="America/Whitehorse"/>
+			<mapZone other="Yukon Standard Time" territory="CA" type="America/Whitehorse America/Dawson"/>
+
+			<!-- (UTC-06:00) Central America -->
+			<mapZone other="Central America Standard Time" territory="001" type="America/Guatemala"/>
+			<mapZone other="Central America Standard Time" territory="BZ" type="America/Belize"/>
+			<mapZone other="Central America Standard Time" territory="CR" type="America/Costa_Rica"/>
+			<mapZone other="Central America Standard Time" territory="EC" type="Pacific/Galapagos"/>
+			<mapZone other="Central America Standard Time" territory="GT" type="America/Guatemala"/>
+			<mapZone other="Central America Standard Time" territory="HN" type="America/Tegucigalpa"/>
+			<mapZone other="Central America Standard Time" territory="NI" type="America/Managua"/>
+			<mapZone other="Central America Standard Time" territory="SV" type="America/El_Salvador"/>
+			<mapZone other="Central America Standard Time" territory="ZZ" type="Etc/GMT+6"/>
+
+			<!-- (UTC-06:00) Central Time (US & Canada) -->
+			<mapZone other="Central Standard Time" territory="001" type="America/Chicago"/>
+			<mapZone other="Central Standard Time" territory="CA" type="America/Winnipeg America/Rankin_Inlet America/Resolute"/>
+			<mapZone other="Central Standard Time" territory="MX" type="America/Matamoros America/Ojinaga"/>
+			<mapZone other="Central Standard Time" territory="US" type="America/Chicago America/Indiana/Knox America/Indiana/Tell_City America/Menominee America/North_Dakota/Beulah America/North_Dakota/Center America/North_Dakota/New_Salem"/>
+
+			<!-- (UTC-06:00) Easter Island -->
+			<mapZone other="Easter Island Standard Time" territory="001" type="Pacific/Easter"/>
+			<mapZone other="Easter Island Standard Time" territory="CL" type="Pacific/Easter"/>
+
+			<!-- (UTC-06:00) Guadalajara, Mexico City, Monterrey -->
+			<mapZone other="Central Standard Time (Mexico)" territory="001" type="America/Mexico_City"/>
+			<mapZone other="Central Standard Time (Mexico)" territory="MX" type="America/Mexico_City America/Bahia_Banderas America/Merida America/Monterrey America/Chihuahua "/>
+
+			<!-- (UTC-06:00) Saskatchewan -->
+			<mapZone other="Canada Central Standard Time" territory="001" type="America/Regina"/>
+			<mapZone other="Canada Central Standard Time" territory="CA" type="America/Regina America/Swift_Current"/>
+
+			<!-- (UTC-05:00) Bogota, Lima, Quito, Rio Branco -->
+			<mapZone other="SA Pacific Standard Time" territory="001" type="America/Bogota"/>
+			<mapZone other="SA Pacific Standard Time" territory="BR" type="America/Rio_Branco America/Eirunepe"/>
+			<mapZone other="SA Pacific Standard Time" territory="CA" type="America/Coral_Harbour"/>
+			<mapZone other="SA Pacific Standard Time" territory="CO" type="America/Bogota"/>
+			<mapZone other="SA Pacific Standard Time" territory="EC" type="America/Guayaquil"/>
+			<mapZone other="SA Pacific Standard Time" territory="JM" type="America/Jamaica"/>
+			<mapZone other="SA Pacific Standard Time" territory="KY" type="America/Cayman"/>
+			<mapZone other="SA Pacific Standard Time" territory="PA" type="America/Panama"/>
+			<mapZone other="SA Pacific Standard Time" territory="PE" type="America/Lima"/>
+			<mapZone other="SA Pacific Standard Time" territory="ZZ" type="Etc/GMT+5"/>
+
+			<!-- (UTC-05:00) Chetumal -->
+			<mapZone other="Eastern Standard Time (Mexico)" territory="001" type="America/Cancun"/>
+			<mapZone other="Eastern Standard Time (Mexico)" territory="MX" type="America/Cancun"/>
+
+			<!-- (UTC-05:00) Eastern Time (US & Canada) -->
+			<mapZone other="Eastern Standard Time" territory="001" type="America/New_York"/>
+			<mapZone other="Eastern Standard Time" territory="BS" type="America/Nassau"/>
+			<mapZone other="Eastern Standard Time" territory="CA" type="America/Toronto America/Iqaluit"/>
+			<mapZone other="Eastern Standard Time" territory="US" type="America/New_York America/Detroit America/Indiana/Petersburg America/Indiana/Vincennes America/Indiana/Winamac America/Kentucky/Monticello America/Louisville"/>
+
+			<!-- (UTC-05:00) Haiti -->
+			<mapZone other="Haiti Standard Time" territory="001" type="America/Port-au-Prince"/>
+			<mapZone other="Haiti Standard Time" territory="HT" type="America/Port-au-Prince"/>
+
+			<!-- (UTC-05:00) Havana -->
+			<mapZone other="Cuba Standard Time" territory="001" type="America/Havana"/>
+			<mapZone other="Cuba Standard Time" territory="CU" type="America/Havana"/>
+
+			<!-- (UTC-05:00) Indiana (East) -->
+			<mapZone other="US Eastern Standard Time" territory="001" type="America/Indianapolis"/>
+			<mapZone other="US Eastern Standard Time" territory="US" type="America/Indianapolis America/Indiana/Marengo America/Indiana/Vevay"/>
+
+			<!-- (UTC-05:00) Turks and Caicos -->
+			<mapZone other="Turks And Caicos Standard Time" territory="001" type="America/Grand_Turk"/>
+			<mapZone other="Turks And Caicos Standard Time" territory="TC" type="America/Grand_Turk"/>
+
+			<!-- (UTC-04:00) Asuncion -->
+			<mapZone other="Paraguay Standard Time" territory="001" type="America/Asuncion"/>
+			<mapZone other="Paraguay Standard Time" territory="PY" type="America/Asuncion"/>
+
+			<!-- (UTC-04:00) Atlantic Time (Canada) -->
+			<mapZone other="Atlantic Standard Time" territory="001" type="America/Halifax"/>
+			<mapZone other="Atlantic Standard Time" territory="BM" type="Atlantic/Bermuda"/>
+			<mapZone other="Atlantic Standard Time" territory="CA" type="America/Halifax America/Glace_Bay America/Goose_Bay America/Moncton"/>
+			<mapZone other="Atlantic Standard Time" territory="GL" type="America/Thule"/>
+
+			<!-- (UTC-04:00) Caracas -->
+			<mapZone other="Venezuela Standard Time" territory="001" type="America/Caracas"/>
+			<mapZone other="Venezuela Standard Time" territory="VE" type="America/Caracas"/>
+
+			<!-- (UTC-04:00) Cuiaba -->
+			<mapZone other="Central Brazilian Standard Time" territory="001" type="America/Cuiaba"/>
+			<mapZone other="Central Brazilian Standard Time" territory="BR" type="America/Cuiaba America/Campo_Grande"/>
+
+			<!-- (UTC-04:00) Georgetown, La Paz, Manaus, San Juan -->
+			<mapZone other="SA Western Standard Time" territory="001" type="America/La_Paz"/>
+			<mapZone other="SA Western Standard Time" territory="AG" type="America/Antigua"/>
+			<mapZone other="SA Western Standard Time" territory="AI" type="America/Anguilla"/>
+			<mapZone other="SA Western Standard Time" territory="AW" type="America/Aruba"/>
+			<mapZone other="SA Western Standard Time" territory="BB" type="America/Barbados"/>
+			<mapZone other="SA Western Standard Time" territory="BL" type="America/St_Barthelemy"/>
+			<mapZone other="SA Western Standard Time" territory="BO" type="America/La_Paz"/>
+			<mapZone other="SA Western Standard Time" territory="BQ" type="America/Kralendijk"/>
+			<mapZone other="SA Western Standard Time" territory="BR" type="America/Manaus America/Boa_Vista America/Porto_Velho"/>
+			<mapZone other="SA Western Standard Time" territory="CA" type="America/Blanc-Sablon"/>
+			<mapZone other="SA Western Standard Time" territory="CW" type="America/Curacao"/>
+			<mapZone other="SA Western Standard Time" territory="DM" type="America/Dominica"/>
+			<mapZone other="SA Western Standard Time" territory="DO" type="America/Santo_Domingo"/>
+			<mapZone other="SA Western Standard Time" territory="GD" type="America/Grenada"/>
+			<mapZone other="SA Western Standard Time" territory="GP" type="America/Guadeloupe"/>
+			<mapZone other="SA Western Standard Time" territory="GY" type="America/Guyana"/>
+			<mapZone other="SA Western Standard Time" territory="KN" type="America/St_Kitts"/>
+			<mapZone other="SA Western Standard Time" territory="LC" type="America/St_Lucia"/>
+			<mapZone other="SA Western Standard Time" territory="MF" type="America/Marigot"/>
+			<mapZone other="SA Western Standard Time" territory="MQ" type="America/Martinique"/>
+			<mapZone other="SA Western Standard Time" territory="MS" type="America/Montserrat"/>
+			<mapZone other="SA Western Standard Time" territory="PR" type="America/Puerto_Rico"/>
+			<mapZone other="SA Western Standard Time" territory="SX" type="America/Lower_Princes"/>
+			<mapZone other="SA Western Standard Time" territory="TT" type="America/Port_of_Spain"/>
+			<mapZone other="SA Western Standard Time" territory="VC" type="America/St_Vincent"/>
+			<mapZone other="SA Western Standard Time" territory="VG" type="America/Tortola"/>
+			<mapZone other="SA Western Standard Time" territory="VI" type="America/St_Thomas"/>
+			<mapZone other="SA Western Standard Time" territory="ZZ" type="Etc/GMT+4"/>
+
+			<!-- (UTC-04:00) Santiago -->
+			<mapZone other="Pacific SA Standard Time" territory="001" type="America/Santiago"/>
+			<mapZone other="Pacific SA Standard Time" territory="CL" type="America/Santiago"/>
+
+			<!-- (UTC-03:30) Newfoundland -->
+			<mapZone other="Newfoundland Standard Time" territory="001" type="America/St_Johns"/>
+			<mapZone other="Newfoundland Standard Time" territory="CA" type="America/St_Johns"/>
+
+			<!-- (UTC-03:00) Araguaina -->
+			<mapZone other="Tocantins Standard Time" territory="001" type="America/Araguaina"/>
+			<mapZone other="Tocantins Standard Time" territory="BR" type="America/Araguaina"/>
+
+			<!-- (UTC-03:00) Brasilia -->
+			<mapZone other="E. South America Standard Time" territory="001" type="America/Sao_Paulo"/>
+			<mapZone other="E. South America Standard Time" territory="BR" type="America/Sao_Paulo"/>
+
+			<!-- (UTC-03:00) Cayenne, Fortaleza -->
+			<mapZone other="SA Eastern Standard Time" territory="001" type="America/Cayenne"/>
+			<mapZone other="SA Eastern Standard Time" territory="AQ" type="Antarctica/Rothera Antarctica/Palmer"/>
+			<mapZone other="SA Eastern Standard Time" territory="BR" type="America/Fortaleza America/Belem America/Maceio America/Recife America/Santarem"/>
+			<mapZone other="SA Eastern Standard Time" territory="FK" type="Atlantic/Stanley"/>
+			<mapZone other="SA Eastern Standard Time" territory="GF" type="America/Cayenne"/>
+			<mapZone other="SA Eastern Standard Time" territory="SR" type="America/Paramaribo"/>
+			<mapZone other="SA Eastern Standard Time" territory="ZZ" type="Etc/GMT+3"/>
+
+			<!-- (UTC-03:00) City of Buenos Aires -->
+			<mapZone other="Argentina Standard Time" territory="001" type="America/Buenos_Aires"/>
+			<mapZone other="Argentina Standard Time" territory="AR" type="America/Buenos_Aires America/Argentina/La_Rioja America/Argentina/Rio_Gallegos America/Argentina/Salta America/Argentina/San_Juan America/Argentina/San_Luis America/Argentina/Tucuman America/Argentina/Ushuaia America/Catamarca America/Cordoba America/Jujuy America/Mendoza"/>
+
+			<!-- (UTC-03:00) Greenland -->
+			<mapZone other="Greenland Standard Time" territory="001" type="America/Godthab"/>
+			<mapZone other="Greenland Standard Time" territory="GL" type="America/Godthab"/>
+
+			<!-- (UTC-03:00) Montevideo -->
+			<mapZone other="Montevideo Standard Time" territory="001" type="America/Montevideo"/>
+			<mapZone other="Montevideo Standard Time" territory="UY" type="America/Montevideo"/>
+
+			<!-- (UTC-03:00) Punta Arenas -->
+			<mapZone other="Magallanes Standard Time" territory="001" type="America/Punta_Arenas"/>
+			<mapZone other="Magallanes Standard Time" territory="CL" type="America/Punta_Arenas"/>
+
+			<!-- (UTC-03:00) Saint Pierre and Miquelon -->
+			<mapZone other="Saint Pierre Standard Time" territory="001" type="America/Miquelon"/>
+			<mapZone other="Saint Pierre Standard Time" territory="PM" type="America/Miquelon"/>
+
+			<!-- (UTC-03:00) Salvador -->
+			<mapZone other="Bahia Standard Time" territory="001" type="America/Bahia"/>
+			<mapZone other="Bahia Standard Time" territory="BR" type="America/Bahia"/>
+
+			<!-- (UTC-02:00) Coordinated Universal Time-02 -->
+			<mapZone other="UTC-02" territory="001" type="Etc/GMT+2"/>
+			<mapZone other="UTC-02" territory="BR" type="America/Noronha"/>
+			<mapZone other="UTC-02" territory="GS" type="Atlantic/South_Georgia"/>
+			<mapZone other="UTC-02" territory="ZZ" type="Etc/GMT+2"/>
+
+			<!-- (UTC-01:00) Azores -->
+			<mapZone other="Azores Standard Time" territory="001" type="Atlantic/Azores"/>
+			<mapZone other="Azores Standard Time" territory="GL" type="America/Scoresbysund"/>
+			<mapZone other="Azores Standard Time" territory="PT" type="Atlantic/Azores"/>
+
+			<!-- (UTC-01:00) Cabo Verde Is. -->
+			<mapZone other="Cape Verde Standard Time" territory="001" type="Atlantic/Cape_Verde"/>
+			<mapZone other="Cape Verde Standard Time" territory="CV" type="Atlantic/Cape_Verde"/>
+			<mapZone other="Cape Verde Standard Time" territory="ZZ" type="Etc/GMT+1"/>
+
+			<!-- (UTC) Coordinated Universal Time -->
+			<mapZone other="UTC" territory="001" type="Etc/UTC"/>
+			<mapZone other="UTC" territory="ZZ" type="Etc/UTC Etc/GMT"/>
+
+			<!-- (UTC+00:00) Dublin, Edinburgh, Lisbon, London -->
+			<mapZone other="GMT Standard Time" territory="001" type="Europe/London"/>
+			<mapZone other="GMT Standard Time" territory="ES" type="Atlantic/Canary"/>
+			<mapZone other="GMT Standard Time" territory="FO" type="Atlantic/Faeroe"/>
+			<mapZone other="GMT Standard Time" territory="GB" type="Europe/London"/>
+			<mapZone other="GMT Standard Time" territory="GG" type="Europe/Guernsey"/>
+			<mapZone other="GMT Standard Time" territory="IE" type="Europe/Dublin"/>
+			<mapZone other="GMT Standard Time" territory="IM" type="Europe/Isle_of_Man"/>
+			<mapZone other="GMT Standard Time" territory="JE" type="Europe/Jersey"/>
+			<mapZone other="GMT Standard Time" territory="PT" type="Europe/Lisbon Atlantic/Madeira"/>
+
+			<!-- (UTC+00:00) Monrovia, Reykjavik -->
+			<mapZone other="Greenwich Standard Time" territory="001" type="Atlantic/Reykjavik"/>
+			<mapZone other="Greenwich Standard Time" territory="BF" type="Africa/Ouagadougou"/>
+			<mapZone other="Greenwich Standard Time" territory="CI" type="Africa/Abidjan"/>
+			<mapZone other="Greenwich Standard Time" territory="GH" type="Africa/Accra"/>
+			<mapZone other="Greenwich Standard Time" territory="GL" type="America/Danmarkshavn"/>
+			<mapZone other="Greenwich Standard Time" territory="GM" type="Africa/Banjul"/>
+			<mapZone other="Greenwich Standard Time" territory="GN" type="Africa/Conakry"/>
+			<mapZone other="Greenwich Standard Time" territory="GW" type="Africa/Bissau"/>
+			<mapZone other="Greenwich Standard Time" territory="IS" type="Atlantic/Reykjavik"/>
+			<mapZone other="Greenwich Standard Time" territory="LR" type="Africa/Monrovia"/>
+			<mapZone other="Greenwich Standard Time" territory="ML" type="Africa/Bamako"/>
+			<mapZone other="Greenwich Standard Time" territory="MR" type="Africa/Nouakchott"/>
+			<mapZone other="Greenwich Standard Time" territory="SH" type="Atlantic/St_Helena"/>
+			<mapZone other="Greenwich Standard Time" territory="SL" type="Africa/Freetown"/>
+			<mapZone other="Greenwich Standard Time" territory="SN" type="Africa/Dakar"/>
+			<mapZone other="Greenwich Standard Time" territory="TG" type="Africa/Lome"/>
+
+			<!-- (UTC+00:00) Sao Tome -->
+			<mapZone other="Sao Tome Standard Time" territory="001" type="Africa/Sao_Tome"/>
+			<mapZone other="Sao Tome Standard Time" territory="ST" type="Africa/Sao_Tome"/>
+
+			<!-- (UTC+01:00) Casablanca -->
+			<mapZone other="Morocco Standard Time" territory="001" type="Africa/Casablanca"/>
+			<mapZone other="Morocco Standard Time" territory="EH" type="Africa/El_Aaiun"/>
+			<mapZone other="Morocco Standard Time" territory="MA" type="Africa/Casablanca"/>
+
+			<!-- (UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna -->
+			<mapZone other="W. Europe Standard Time" territory="001" type="Europe/Berlin"/>
+			<mapZone other="W. Europe Standard Time" territory="AD" type="Europe/Andorra"/>
+			<mapZone other="W. Europe Standard Time" territory="AT" type="Europe/Vienna"/>
+			<mapZone other="W. Europe Standard Time" territory="CH" type="Europe/Zurich"/>
+			<mapZone other="W. Europe Standard Time" territory="DE" type="Europe/Berlin Europe/Busingen"/>
+			<mapZone other="W. Europe Standard Time" territory="GI" type="Europe/Gibraltar"/>
+			<mapZone other="W. Europe Standard Time" territory="IT" type="Europe/Rome"/>
+			<mapZone other="W. Europe Standard Time" territory="LI" type="Europe/Vaduz"/>
+			<mapZone other="W. Europe Standard Time" territory="LU" type="Europe/Luxembourg"/>
+			<mapZone other="W. Europe Standard Time" territory="MC" type="Europe/Monaco"/>
+			<mapZone other="W. Europe Standard Time" territory="MT" type="Europe/Malta"/>
+			<mapZone other="W. Europe Standard Time" territory="NL" type="Europe/Amsterdam"/>
+			<mapZone other="W. Europe Standard Time" territory="NO" type="Europe/Oslo"/>
+			<mapZone other="W. Europe Standard Time" territory="SE" type="Europe/Stockholm"/>
+			<mapZone other="W. Europe Standard Time" territory="SJ" type="Arctic/Longyearbyen"/>
+			<mapZone other="W. Europe Standard Time" territory="SM" type="Europe/San_Marino"/>
+			<mapZone other="W. Europe Standard Time" territory="VA" type="Europe/Vatican"/>
+
+			<!-- (UTC+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague -->
+			<mapZone other="Central Europe Standard Time" territory="001" type="Europe/Budapest"/>
+			<mapZone other="Central Europe Standard Time" territory="AL" type="Europe/Tirane"/>
+			<mapZone other="Central Europe Standard Time" territory="CZ" type="Europe/Prague"/>
+			<mapZone other="Central Europe Standard Time" territory="HU" type="Europe/Budapest"/>
+			<mapZone other="Central Europe Standard Time" territory="ME" type="Europe/Podgorica"/>
+			<mapZone other="Central Europe Standard Time" territory="RS" type="Europe/Belgrade"/>
+			<mapZone other="Central Europe Standard Time" territory="SI" type="Europe/Ljubljana"/>
+			<mapZone other="Central Europe Standard Time" territory="SK" type="Europe/Bratislava"/>
+
+			<!-- (UTC+01:00) Brussels, Copenhagen, Madrid, Paris -->
+			<mapZone other="Romance Standard Time" territory="001" type="Europe/Paris"/>
+			<mapZone other="Romance Standard Time" territory="BE" type="Europe/Brussels"/>
+			<mapZone other="Romance Standard Time" territory="DK" type="Europe/Copenhagen"/>
+			<mapZone other="Romance Standard Time" territory="ES" type="Europe/Madrid Africa/Ceuta"/>
+			<mapZone other="Romance Standard Time" territory="FR" type="Europe/Paris"/>
+
+			<!-- (UTC+01:00) Sarajevo, Skopje, Warsaw, Zagreb -->
+			<mapZone other="Central European Standard Time" territory="001" type="Europe/Warsaw"/>
+			<mapZone other="Central European Standard Time" territory="BA" type="Europe/Sarajevo"/>
+			<mapZone other="Central European Standard Time" territory="HR" type="Europe/Zagreb"/>
+			<mapZone other="Central European Standard Time" territory="MK" type="Europe/Skopje"/>
+			<mapZone other="Central European Standard Time" territory="PL" type="Europe/Warsaw"/>
+
+			<!-- (UTC+01:00) West Central Africa -->
+			<mapZone other="W. Central Africa Standard Time" territory="001" type="Africa/Lagos"/>
+			<mapZone other="W. Central Africa Standard Time" territory="AO" type="Africa/Luanda"/>
+			<mapZone other="W. Central Africa Standard Time" territory="BJ" type="Africa/Porto-Novo"/>
+			<mapZone other="W. Central Africa Standard Time" territory="CD" type="Africa/Kinshasa"/>
+			<mapZone other="W. Central Africa Standard Time" territory="CF" type="Africa/Bangui"/>
+			<mapZone other="W. Central Africa Standard Time" territory="CG" type="Africa/Brazzaville"/>
+			<mapZone other="W. Central Africa Standard Time" territory="CM" type="Africa/Douala"/>
+			<mapZone other="W. Central Africa Standard Time" territory="DZ" type="Africa/Algiers"/>
+			<mapZone other="W. Central Africa Standard Time" territory="GA" type="Africa/Libreville"/>
+			<mapZone other="W. Central Africa Standard Time" territory="GQ" type="Africa/Malabo"/>
+			<mapZone other="W. Central Africa Standard Time" territory="NE" type="Africa/Niamey"/>
+			<mapZone other="W. Central Africa Standard Time" territory="NG" type="Africa/Lagos"/>
+			<mapZone other="W. Central Africa Standard Time" territory="TD" type="Africa/Ndjamena"/>
+			<mapZone other="W. Central Africa Standard Time" territory="TN" type="Africa/Tunis"/>
+			<mapZone other="W. Central Africa Standard Time" territory="ZZ" type="Etc/GMT-1"/>
+
+			<!-- (UTC+02:00) Amman -->
+			<mapZone other="Jordan Standard Time" territory="001" type="Asia/Amman"/>
+			<mapZone other="Jordan Standard Time" territory="JO" type="Asia/Amman"/>
+
+			<!-- (UTC+02:00) Athens, Bucharest -->
+			<mapZone other="GTB Standard Time" territory="001" type="Europe/Bucharest"/>
+			<mapZone other="GTB Standard Time" territory="CY" type="Asia/Nicosia Asia/Famagusta"/>
+			<mapZone other="GTB Standard Time" territory="GR" type="Europe/Athens"/>
+			<mapZone other="GTB Standard Time" territory="RO" type="Europe/Bucharest"/>
+
+			<!-- (UTC+02:00) Beirut -->
+			<mapZone other="Middle East Standard Time" territory="001" type="Asia/Beirut"/>
+			<mapZone other="Middle East Standard Time" territory="LB" type="Asia/Beirut"/>
+
+			<!-- (UTC+02:00) Cairo -->
+			<mapZone other="Egypt Standard Time" territory="001" type="Africa/Cairo"/>
+			<mapZone other="Egypt Standard Time" territory="EG" type="Africa/Cairo"/>
+
+			<!-- (UTC+02:00) Chisinau -->
+			<mapZone other="E. Europe Standard Time" territory="001" type="Europe/Chisinau"/>
+			<mapZone other="E. Europe Standard Time" territory="MD" type="Europe/Chisinau"/>
+
+			<!-- (UTC+02:00) Damascus -->
+			<mapZone other="Syria Standard Time" territory="001" type="Asia/Damascus"/>
+			<mapZone other="Syria Standard Time" territory="SY" type="Asia/Damascus"/>
+
+			<!-- (UTC+02:00) Gaza, Hebron -->
+			<mapZone other="West Bank Standard Time" territory="001" type="Asia/Hebron"/>
+			<mapZone other="West Bank Standard Time" territory="PS" type="Asia/Hebron Asia/Gaza"/>
+
+			<!-- (UTC+02:00) Harare, Pretoria -->
+			<mapZone other="South Africa Standard Time" territory="001" type="Africa/Johannesburg"/>
+			<mapZone other="South Africa Standard Time" territory="BI" type="Africa/Bujumbura"/>
+			<mapZone other="South Africa Standard Time" territory="BW" type="Africa/Gaborone"/>
+			<mapZone other="South Africa Standard Time" territory="CD" type="Africa/Lubumbashi"/>
+			<mapZone other="South Africa Standard Time" territory="LS" type="Africa/Maseru"/>
+			<mapZone other="South Africa Standard Time" territory="MW" type="Africa/Blantyre"/>
+			<mapZone other="South Africa Standard Time" territory="MZ" type="Africa/Maputo"/>
+			<mapZone other="South Africa Standard Time" territory="RW" type="Africa/Kigali"/>
+			<mapZone other="South Africa Standard Time" territory="SZ" type="Africa/Mbabane"/>
+			<mapZone other="South Africa Standard Time" territory="ZA" type="Africa/Johannesburg"/>
+			<mapZone other="South Africa Standard Time" territory="ZM" type="Africa/Lusaka"/>
+			<mapZone other="South Africa Standard Time" territory="ZW" type="Africa/Harare"/>
+			<mapZone other="South Africa Standard Time" territory="ZZ" type="Etc/GMT-2"/>
+
+			<!-- (UTC+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius -->
+			<mapZone other="FLE Standard Time" territory="001" type="Europe/Kiev"/>
+			<mapZone other="FLE Standard Time" territory="AX" type="Europe/Mariehamn"/>
+			<mapZone other="FLE Standard Time" territory="BG" type="Europe/Sofia"/>
+			<mapZone other="FLE Standard Time" territory="EE" type="Europe/Tallinn"/>
+			<mapZone other="FLE Standard Time" territory="FI" type="Europe/Helsinki"/>
+			<mapZone other="FLE Standard Time" territory="LT" type="Europe/Vilnius"/>
+			<mapZone other="FLE Standard Time" territory="LV" type="Europe/Riga"/>
+			<mapZone other="FLE Standard Time" territory="UA" type="Europe/Kiev"/>
+
+			<!-- (UTC+02:00) Jerusalem -->
+			<mapZone other="Israel Standard Time" territory="001" type="Asia/Jerusalem"/>
+			<mapZone other="Israel Standard Time" territory="IL" type="Asia/Jerusalem"/>
+
+			<!-- (UTC+02:00) Juba -->
+			<mapZone other="South Sudan Standard Time" territory="001" type="Africa/Juba"/>
+			<mapZone other="South Sudan Standard Time" territory="SS" type="Africa/Juba"/>
+
+			<!-- (UTC+02:00) Kaliningrad -->
+			<mapZone other="Kaliningrad Standard Time" territory="001" type="Europe/Kaliningrad"/>
+			<mapZone other="Kaliningrad Standard Time" territory="RU" type="Europe/Kaliningrad"/>
+
+			<!-- (UTC+02:00) Khartoum -->
+			<mapZone other="Sudan Standard Time" territory="001" type="Africa/Khartoum"/>
+			<mapZone other="Sudan Standard Time" territory="SD" type="Africa/Khartoum"/>
+
+			<!-- (UTC+02:00) Tripoli -->
+			<mapZone other="Libya Standard Time" territory="001" type="Africa/Tripoli"/>
+			<mapZone other="Libya Standard Time" territory="LY" type="Africa/Tripoli"/>
+
+			<!-- (UTC+02:00) Windhoek -->
+			<mapZone other="Namibia Standard Time" territory="001" type="Africa/Windhoek"/>
+			<mapZone other="Namibia Standard Time" territory="NA" type="Africa/Windhoek"/>
+
+			<!-- (UTC+03:00) Baghdad -->
+			<mapZone other="Arabic Standard Time" territory="001" type="Asia/Baghdad"/>
+			<mapZone other="Arabic Standard Time" territory="IQ" type="Asia/Baghdad"/>
+
+			<!-- (UTC+03:00) Istanbul -->
+			<mapZone other="Turkey Standard Time" territory="001" type="Europe/Istanbul"/>
+			<mapZone other="Turkey Standard Time" territory="TR" type="Europe/Istanbul"/>
+
+			<!-- (UTC+03:00) Kuwait, Riyadh -->
+			<mapZone other="Arab Standard Time" territory="001" type="Asia/Riyadh"/>
+			<mapZone other="Arab Standard Time" territory="BH" type="Asia/Bahrain"/>
+			<mapZone other="Arab Standard Time" territory="KW" type="Asia/Kuwait"/>
+			<mapZone other="Arab Standard Time" territory="QA" type="Asia/Qatar"/>
+			<mapZone other="Arab Standard Time" territory="SA" type="Asia/Riyadh"/>
+			<mapZone other="Arab Standard Time" territory="YE" type="Asia/Aden"/>
+
+			<!-- (UTC+03:00) Minsk -->
+			<mapZone other="Belarus Standard Time" territory="001" type="Europe/Minsk"/>
+			<mapZone other="Belarus Standard Time" territory="BY" type="Europe/Minsk"/>
+
+			<!-- (UTC+03:00) Moscow, St. Petersburg -->
+			<mapZone other="Russian Standard Time" territory="001" type="Europe/Moscow"/>
+			<mapZone other="Russian Standard Time" territory="RU" type="Europe/Moscow Europe/Kirov"/>
+			<mapZone other="Russian Standard Time" territory="UA" type="Europe/Simferopol"/>
+
+			<!-- (UTC+03:00) Nairobi -->
+			<mapZone other="E. Africa Standard Time" territory="001" type="Africa/Nairobi"/>
+			<mapZone other="E. Africa Standard Time" territory="AQ" type="Antarctica/Syowa"/>
+			<mapZone other="E. Africa Standard Time" territory="DJ" type="Africa/Djibouti"/>
+			<mapZone other="E. Africa Standard Time" territory="ER" type="Africa/Asmera"/>
+			<mapZone other="E. Africa Standard Time" territory="ET" type="Africa/Addis_Ababa"/>
+			<mapZone other="E. Africa Standard Time" territory="KE" type="Africa/Nairobi"/>
+			<mapZone other="E. Africa Standard Time" territory="KM" type="Indian/Comoro"/>
+			<mapZone other="E. Africa Standard Time" territory="MG" type="Indian/Antananarivo"/>
+			<mapZone other="E. Africa Standard Time" territory="SO" type="Africa/Mogadishu"/>
+			<mapZone other="E. Africa Standard Time" territory="TZ" type="Africa/Dar_es_Salaam"/>
+			<mapZone other="E. Africa Standard Time" territory="UG" type="Africa/Kampala"/>
+			<mapZone other="E. Africa Standard Time" territory="YT" type="Indian/Mayotte"/>
+			<mapZone other="E. Africa Standard Time" territory="ZZ" type="Etc/GMT-3"/>
+
+			<!-- (UTC+03:30) Tehran -->
+			<mapZone other="Iran Standard Time" territory="001" type="Asia/Tehran"/>
+			<mapZone other="Iran Standard Time" territory="IR" type="Asia/Tehran"/>
+
+			<!-- (UTC+04:00) Abu Dhabi, Muscat -->
+			<mapZone other="Arabian Standard Time" territory="001" type="Asia/Dubai"/>
+			<mapZone other="Arabian Standard Time" territory="AE" type="Asia/Dubai"/>
+			<mapZone other="Arabian Standard Time" territory="OM" type="Asia/Muscat"/>
+			<mapZone other="Arabian Standard Time" territory="ZZ" type="Etc/GMT-4"/>
+
+			<!-- (UTC+04:00) Astrakhan, Ulyanovsk -->
+			<mapZone other="Astrakhan Standard Time" territory="001" type="Europe/Astrakhan"/>
+			<mapZone other="Astrakhan Standard Time" territory="RU" type="Europe/Astrakhan Europe/Ulyanovsk"/>
+
+			<!-- (UTC+04:00) Baku -->
+			<mapZone other="Azerbaijan Standard Time" territory="001" type="Asia/Baku"/>
+			<mapZone other="Azerbaijan Standard Time" territory="AZ" type="Asia/Baku"/>
+
+			<!-- (UTC+04:00) Izhevsk, Samara -->
+			<mapZone other="Russia Time Zone 3" territory="001" type="Europe/Samara"/>
+			<mapZone other="Russia Time Zone 3" territory="RU" type="Europe/Samara"/>
+
+			<!-- (UTC+04:00) Port Louis -->
+			<mapZone other="Mauritius Standard Time" territory="001" type="Indian/Mauritius"/>
+			<mapZone other="Mauritius Standard Time" territory="MU" type="Indian/Mauritius"/>
+			<mapZone other="Mauritius Standard Time" territory="RE" type="Indian/Reunion"/>
+			<mapZone other="Mauritius Standard Time" territory="SC" type="Indian/Mahe"/>
+
+			<!-- (UTC+04:00) Saratov -->
+			<mapZone other="Saratov Standard Time" territory="001" type="Europe/Saratov"/>
+			<mapZone other="Saratov Standard Time" territory="RU" type="Europe/Saratov"/>
+
+			<!-- (UTC+04:00) Tbilisi -->
+			<mapZone other="Georgian Standard Time" territory="001" type="Asia/Tbilisi"/>
+			<mapZone other="Georgian Standard Time" territory="GE" type="Asia/Tbilisi"/>
+
+			<!-- (UTC+04:00) Volgograd -->
+			<mapZone other="Volgograd Standard Time" territory="001" type="Europe/Volgograd"/>
+			<mapZone other="Volgograd Standard Time" territory="RU" type="Europe/Volgograd"/>
+
+			<!-- (UTC+04:00) Yerevan -->
+			<mapZone other="Caucasus Standard Time" territory="001" type="Asia/Yerevan"/>
+			<mapZone other="Caucasus Standard Time" territory="AM" type="Asia/Yerevan"/>
+
+			<!-- (UTC+04:30) Kabul -->
+			<mapZone other="Afghanistan Standard Time" territory="001" type="Asia/Kabul"/>
+			<mapZone other="Afghanistan Standard Time" territory="AF" type="Asia/Kabul"/>
+
+			<!-- (UTC+05:00) Ashgabat, Tashkent -->
+			<mapZone other="West Asia Standard Time" territory="001" type="Asia/Tashkent"/>
+			<mapZone other="West Asia Standard Time" territory="AQ" type="Antarctica/Mawson"/>
+			<!-- Microsoft may create a new zone dedicated for Almaty and Qostanay. -->
+			<mapZone other="West Asia Standard Time" territory="KZ" type="Asia/Oral Asia/Almaty Asia/Aqtau Asia/Aqtobe Asia/Atyrau Asia/Qostanay"/>
+			<mapZone other="West Asia Standard Time" territory="MV" type="Indian/Maldives"/>
+			<mapZone other="West Asia Standard Time" territory="TF" type="Indian/Kerguelen"/>
+			<mapZone other="West Asia Standard Time" territory="TJ" type="Asia/Dushanbe"/>
+			<mapZone other="West Asia Standard Time" territory="TM" type="Asia/Ashgabat"/>
+			<mapZone other="West Asia Standard Time" territory="UZ" type="Asia/Tashkent Asia/Samarkand"/>
+			<mapZone other="West Asia Standard Time" territory="ZZ" type="Etc/GMT-5"/>
+
+			<!-- (UTC+05:00) Ekaterinburg -->
+			<mapZone other="Ekaterinburg Standard Time" territory="001" type="Asia/Yekaterinburg"/>
+			<mapZone other="Ekaterinburg Standard Time" territory="RU" type="Asia/Yekaterinburg"/>
+
+			<!-- (UTC+05:00) Islamabad, Karachi -->
+			<mapZone other="Pakistan Standard Time" territory="001" type="Asia/Karachi"/>
+			<mapZone other="Pakistan Standard Time" territory="PK" type="Asia/Karachi"/>
+
+			<!-- (UTC+05:00) Qyzylorda -->
+			<mapZone other="Qyzylorda Standard Time" territory="001" type="Asia/Qyzylorda"/>
+			<mapZone other="Qyzylorda Standard Time" territory="KZ" type="Asia/Qyzylorda"/>
+
+			<!-- (UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi -->
+			<mapZone other="India Standard Time" territory="001" type="Asia/Calcutta"/>
+			<mapZone other="India Standard Time" territory="IN" type="Asia/Calcutta"/>
+
+			<!-- (UTC+05:30) Sri Jayawardenepura -->
+			<mapZone other="Sri Lanka Standard Time" territory="001" type="Asia/Colombo"/>
+			<mapZone other="Sri Lanka Standard Time" territory="LK" type="Asia/Colombo"/>
+
+			<!-- (UTC+05:45) Kathmandu -->
+			<mapZone other="Nepal Standard Time" territory="001" type="Asia/Katmandu"/>
+			<mapZone other="Nepal Standard Time" territory="NP" type="Asia/Katmandu"/>
+
+			<!-- (UTC+06:00) Astana --> <!-- Microsoft probably keeps Central Asia Standard Time, but change Astana to something else. -->
+			<mapZone other="Central Asia Standard Time" territory="001" type="Asia/Bishkek"/>
+			<mapZone other="Central Asia Standard Time" territory="AQ" type="Antarctica/Vostok"/>
+			<mapZone other="Central Asia Standard Time" territory="CN" type="Asia/Urumqi"/>
+			<mapZone other="Central Asia Standard Time" territory="IO" type="Indian/Chagos"/>
+			<mapZone other="Central Asia Standard Time" territory="KG" type="Asia/Bishkek"/>
+			<mapZone other="Central Asia Standard Time" territory="ZZ" type="Etc/GMT-6"/>
+
+			<!-- (UTC+06:00) Dhaka -->
+			<mapZone other="Bangladesh Standard Time" territory="001" type="Asia/Dhaka"/>
+			<mapZone other="Bangladesh Standard Time" territory="BD" type="Asia/Dhaka"/>
+			<mapZone other="Bangladesh Standard Time" territory="BT" type="Asia/Thimphu"/>
+
+			<!-- (UTC+06:00) Omsk -->
+			<mapZone other="Omsk Standard Time" territory="001" type="Asia/Omsk"/>
+			<mapZone other="Omsk Standard Time" territory="RU" type="Asia/Omsk"/>
+
+			<!-- (UTC+06:30) Yangon (Rangoon) -->
+			<mapZone other="Myanmar Standard Time" territory="001" type="Asia/Rangoon"/>
+			<mapZone other="Myanmar Standard Time" territory="CC" type="Indian/Cocos"/>
+			<mapZone other="Myanmar Standard Time" territory="MM" type="Asia/Rangoon"/>
+
+			<!-- (UTC+07:00) Bangkok, Hanoi, Jakarta -->
+			<mapZone other="SE Asia Standard Time" territory="001" type="Asia/Bangkok"/>
+			<mapZone other="SE Asia Standard Time" territory="AQ" type="Antarctica/Davis"/>
+			<mapZone other="SE Asia Standard Time" territory="CX" type="Indian/Christmas"/>
+			<mapZone other="SE Asia Standard Time" territory="ID" type="Asia/Jakarta Asia/Pontianak"/>
+			<mapZone other="SE Asia Standard Time" territory="KH" type="Asia/Phnom_Penh"/>
+			<mapZone other="SE Asia Standard Time" territory="LA" type="Asia/Vientiane"/>
+			<mapZone other="SE Asia Standard Time" territory="TH" type="Asia/Bangkok"/>
+			<mapZone other="SE Asia Standard Time" territory="VN" type="Asia/Saigon"/>
+			<mapZone other="SE Asia Standard Time" territory="ZZ" type="Etc/GMT-7"/>
+
+			<!-- (UTC+07:00) Barnaul, Gorno-Altaysk -->
+			<mapZone other="Altai Standard Time" territory="001" type="Asia/Barnaul"/>
+			<mapZone other="Altai Standard Time" territory="RU" type="Asia/Barnaul"/>
+
+			<!-- (UTC+07:00) Hovd -->
+			<mapZone other="W. Mongolia Standard Time" territory="001" type="Asia/Hovd"/>
+			<mapZone other="W. Mongolia Standard Time" territory="MN" type="Asia/Hovd"/>
+
+			<!-- (UTC+07:00) Krasnoyarsk -->
+			<mapZone other="North Asia Standard Time" territory="001" type="Asia/Krasnoyarsk"/>
+			<mapZone other="North Asia Standard Time" territory="RU" type="Asia/Krasnoyarsk Asia/Novokuznetsk"/>
+
+			<!-- (UTC+07:00) Novosibirsk -->
+			<mapZone other="N. Central Asia Standard Time" territory="001" type="Asia/Novosibirsk"/>
+			<mapZone other="N. Central Asia Standard Time" territory="RU" type="Asia/Novosibirsk"/>
+
+			<!-- (UTC+07:00) Tomsk -->
+			<mapZone other="Tomsk Standard Time" territory="001" type="Asia/Tomsk"/>
+			<mapZone other="Tomsk Standard Time" territory="RU" type="Asia/Tomsk"/>
+
+			<!-- (UTC+08:00) Beijing, Chongqing, Hong Kong, Urumqi -->
+			<mapZone other="China Standard Time" territory="001" type="Asia/Shanghai"/>
+			<mapZone other="China Standard Time" territory="CN" type="Asia/Shanghai"/>
+			<mapZone other="China Standard Time" territory="HK" type="Asia/Hong_Kong"/>
+			<mapZone other="China Standard Time" territory="MO" type="Asia/Macau"/>
+
+			<!-- (UTC+08:00) Irkutsk -->
+			<mapZone other="North Asia East Standard Time" territory="001" type="Asia/Irkutsk"/>
+			<mapZone other="North Asia East Standard Time" territory="RU" type="Asia/Irkutsk"/>
+
+			<!-- (UTC+08:00) Kuala Lumpur, Singapore -->
+			<mapZone other="Singapore Standard Time" territory="001" type="Asia/Singapore"/>
+			<mapZone other="Singapore Standard Time" territory="BN" type="Asia/Brunei"/>
+			<mapZone other="Singapore Standard Time" territory="ID" type="Asia/Makassar"/>
+			<mapZone other="Singapore Standard Time" territory="MY" type="Asia/Kuala_Lumpur Asia/Kuching"/>
+			<mapZone other="Singapore Standard Time" territory="PH" type="Asia/Manila"/>
+			<mapZone other="Singapore Standard Time" territory="SG" type="Asia/Singapore"/>
+			<mapZone other="Singapore Standard Time" territory="ZZ" type="Etc/GMT-8"/>
+
+			<!-- (UTC+08:00) Perth -->
+			<mapZone other="W. Australia Standard Time" territory="001" type="Australia/Perth"/>
+			<mapZone other="W. Australia Standard Time" territory="AU" type="Australia/Perth"/>
+
+			<!-- (UTC+08:00) Taipei -->
+			<mapZone other="Taipei Standard Time" territory="001" type="Asia/Taipei"/>
+			<mapZone other="Taipei Standard Time" territory="TW" type="Asia/Taipei"/>
+
+			<!-- (UTC+08:00) Ulaanbaatar -->
+			<mapZone other="Ulaanbaatar Standard Time" territory="001" type="Asia/Ulaanbaatar"/>
+			<mapZone other="Ulaanbaatar Standard Time" territory="MN" type="Asia/Ulaanbaatar Asia/Choibalsan"/>
+
+			<!-- (UTC+08:45) Eucla -->
+			<mapZone other="Aus Central W. Standard Time" territory="001" type="Australia/Eucla"/>
+			<mapZone other="Aus Central W. Standard Time" territory="AU" type="Australia/Eucla"/>
+
+			<!-- (UTC+09:00) Chita -->
+			<mapZone other="Transbaikal Standard Time" territory="001" type="Asia/Chita"/>
+			<mapZone other="Transbaikal Standard Time" territory="RU" type="Asia/Chita"/>
+
+			<!-- (UTC+09:00) Osaka, Sapporo, Tokyo -->
+			<mapZone other="Tokyo Standard Time" territory="001" type="Asia/Tokyo"/>
+			<mapZone other="Tokyo Standard Time" territory="ID" type="Asia/Jayapura"/>
+			<mapZone other="Tokyo Standard Time" territory="JP" type="Asia/Tokyo"/>
+			<mapZone other="Tokyo Standard Time" territory="PW" type="Pacific/Palau"/>
+			<mapZone other="Tokyo Standard Time" territory="TL" type="Asia/Dili"/>
+			<mapZone other="Tokyo Standard Time" territory="ZZ" type="Etc/GMT-9"/>
+
+			<!-- (UTC+09:00) Pyongyang -->
+			<mapZone other="North Korea Standard Time" territory="001" type="Asia/Pyongyang"/>
+			<mapZone other="North Korea Standard Time" territory="KP" type="Asia/Pyongyang"/>
+
+			<!-- (UTC+09:00) Seoul -->
+			<mapZone other="Korea Standard Time" territory="001" type="Asia/Seoul"/>
+			<mapZone other="Korea Standard Time" territory="KR" type="Asia/Seoul"/>
+
+			<!-- (UTC+09:00) Yakutsk -->
+			<mapZone other="Yakutsk Standard Time" territory="001" type="Asia/Yakutsk"/>
+			<mapZone other="Yakutsk Standard Time" territory="RU" type="Asia/Yakutsk Asia/Khandyga"/>
+
+			<!-- (UTC+09:30) Adelaide -->
+			<mapZone other="Cen. Australia Standard Time" territory="001" type="Australia/Adelaide"/>
+			<mapZone other="Cen. Australia Standard Time" territory="AU" type="Australia/Adelaide Australia/Broken_Hill"/>
+
+			<!-- (UTC+09:30) Darwin -->
+			<mapZone other="AUS Central Standard Time" territory="001" type="Australia/Darwin"/>
+			<mapZone other="AUS Central Standard Time" territory="AU" type="Australia/Darwin"/>
+
+			<!-- (UTC+10:00) Brisbane -->
+			<mapZone other="E. Australia Standard Time" territory="001" type="Australia/Brisbane"/>
+			<mapZone other="E. Australia Standard Time" territory="AU" type="Australia/Brisbane Australia/Lindeman"/>
+
+			<!-- (UTC+10:00) Canberra, Melbourne, Sydney -->
+			<mapZone other="AUS Eastern Standard Time" territory="001" type="Australia/Sydney"/>
+			<mapZone other="AUS Eastern Standard Time" territory="AU" type="Australia/Sydney Australia/Melbourne"/>
+
+			<!-- (UTC+10:00) Guam, Port Moresby -->
+			<mapZone other="West Pacific Standard Time" territory="001" type="Pacific/Port_Moresby"/>
+			<mapZone other="West Pacific Standard Time" territory="AQ" type="Antarctica/DumontDUrville"/>
+			<mapZone other="West Pacific Standard Time" territory="FM" type="Pacific/Truk"/>
+			<mapZone other="West Pacific Standard Time" territory="GU" type="Pacific/Guam"/>
+			<mapZone other="West Pacific Standard Time" territory="MP" type="Pacific/Saipan"/>
+			<mapZone other="West Pacific Standard Time" territory="PG" type="Pacific/Port_Moresby"/>
+			<mapZone other="West Pacific Standard Time" territory="ZZ" type="Etc/GMT-10"/>
+
+			<!-- (UTC+10:00) Hobart -->
+			<mapZone other="Tasmania Standard Time" territory="001" type="Australia/Hobart"/>
+			<mapZone other="Tasmania Standard Time" territory="AU" type="Australia/Hobart Antarctica/Macquarie"/>
+
+			<!-- (UTC+10:00) Vladivostok -->
+			<mapZone other="Vladivostok Standard Time" territory="001" type="Asia/Vladivostok"/>
+			<mapZone other="Vladivostok Standard Time" territory="RU" type="Asia/Vladivostok Asia/Ust-Nera"/>
+
+			<!-- (UTC+10:30) Lord Howe Island -->
+			<mapZone other="Lord Howe Standard Time" territory="001" type="Australia/Lord_Howe"/>
+			<mapZone other="Lord Howe Standard Time" territory="AU" type="Australia/Lord_Howe"/>
+
+			<!-- (UTC+11:00) Bougainville Island -->
+			<mapZone other="Bougainville Standard Time" territory="001" type="Pacific/Bougainville"/>
+			<mapZone other="Bougainville Standard Time" territory="PG" type="Pacific/Bougainville"/>
+
+			<!-- (UTC+11:00) Chokurdakh -->
+			<mapZone other="Russia Time Zone 10" territory="001" type="Asia/Srednekolymsk"/>
+			<mapZone other="Russia Time Zone 10" territory="RU" type="Asia/Srednekolymsk"/>
+
+			<!-- (UTC+11:00) Magadan -->
+			<mapZone other="Magadan Standard Time" territory="001" type="Asia/Magadan"/>
+			<mapZone other="Magadan Standard Time" territory="RU" type="Asia/Magadan"/>
+
+			<!-- (UTC+11:00) Norfolk Island -->
+			<mapZone other="Norfolk Standard Time" territory="001" type="Pacific/Norfolk"/>
+			<mapZone other="Norfolk Standard Time" territory="NF" type="Pacific/Norfolk"/>
+
+			<!-- (UTC+11:00) Sakhalin -->
+			<mapZone other="Sakhalin Standard Time" territory="001" type="Asia/Sakhalin"/>
+			<mapZone other="Sakhalin Standard Time" territory="RU" type="Asia/Sakhalin"/>
+
+			<!-- (UTC+11:00) Solomon Is., New Caledonia -->
+			<mapZone other="Central Pacific Standard Time" territory="001" type="Pacific/Guadalcanal"/>
+			<mapZone other="Central Pacific Standard Time" territory="AQ" type="Antarctica/Casey"/>
+			<mapZone other="Central Pacific Standard Time" territory="FM" type="Pacific/Ponape Pacific/Kosrae"/>
+			<mapZone other="Central Pacific Standard Time" territory="NC" type="Pacific/Noumea"/>
+			<mapZone other="Central Pacific Standard Time" territory="SB" type="Pacific/Guadalcanal"/>
+			<mapZone other="Central Pacific Standard Time" territory="VU" type="Pacific/Efate"/>
+			<mapZone other="Central Pacific Standard Time" territory="ZZ" type="Etc/GMT-11"/>
+
+			<!-- (UTC+12:00) Anadyr, Petropavlovsk-Kamchatsky -->
+			<mapZone other="Russia Time Zone 11" territory="001" type="Asia/Kamchatka"/>
+			<mapZone other="Russia Time Zone 11" territory="RU" type="Asia/Kamchatka Asia/Anadyr"/>
+
+			<!-- (UTC+12:00) Auckland, Wellington -->
+			<mapZone other="New Zealand Standard Time" territory="001" type="Pacific/Auckland"/>
+			<mapZone other="New Zealand Standard Time" territory="AQ" type="Antarctica/McMurdo"/>
+			<mapZone other="New Zealand Standard Time" territory="NZ" type="Pacific/Auckland"/>
+
+			<!-- (UTC+12:00) Coordinated Universal Time+12 -->
+			<mapZone other="UTC+12" territory="001" type="Etc/GMT-12"/>
+			<mapZone other="UTC+12" territory="KI" type="Pacific/Tarawa"/>
+			<mapZone other="UTC+12" territory="MH" type="Pacific/Majuro Pacific/Kwajalein"/>
+			<mapZone other="UTC+12" territory="NR" type="Pacific/Nauru"/>
+			<mapZone other="UTC+12" territory="TV" type="Pacific/Funafuti"/>
+			<mapZone other="UTC+12" territory="UM" type="Pacific/Wake"/>
+			<mapZone other="UTC+12" territory="WF" type="Pacific/Wallis"/>
+			<mapZone other="UTC+12" territory="ZZ" type="Etc/GMT-12"/>
+
+			<!-- (UTC+12:00) Fiji -->
+			<mapZone other="Fiji Standard Time" territory="001" type="Pacific/Fiji"/>
+			<mapZone other="Fiji Standard Time" territory="FJ" type="Pacific/Fiji"/>
+
+			<!-- (UTC+12:45) Chatham Islands -->
+			<mapZone other="Chatham Islands Standard Time" territory="001" type="Pacific/Chatham"/>
+			<mapZone other="Chatham Islands Standard Time" territory="NZ" type="Pacific/Chatham"/>
+
+			<!-- (UTC+13:00) Coordinated Universal Time+13 -->
+			<mapZone other="UTC+13" territory="001" type="Etc/GMT-13"/>
+			<mapZone other="UTC+13" territory="KI" type="Pacific/Enderbury"/>
+			<mapZone other="UTC+13" territory="TK" type="Pacific/Fakaofo"/>
+			<mapZone other="UTC+13" territory="ZZ" type="Etc/GMT-13"/>
+
+			<!-- (UTC+13:00) Nuku'alofa -->
+			<mapZone other="Tonga Standard Time" territory="001" type="Pacific/Tongatapu"/>
+			<mapZone other="Tonga Standard Time" territory="TO" type="Pacific/Tongatapu"/>
+
+			<!-- (UTC+13:00) Samoa -->
+			<mapZone other="Samoa Standard Time" territory="001" type="Pacific/Apia"/>
+			<mapZone other="Samoa Standard Time" territory="WS" type="Pacific/Apia"/>
+
+			<!-- (UTC+14:00) Kiritimati Island -->
+			<mapZone other="Line Islands Standard Time" territory="001" type="Pacific/Kiritimati"/>
+			<mapZone other="Line Islands Standard Time" territory="KI" type="Pacific/Kiritimati"/>
+			<mapZone other="Line Islands Standard Time" territory="ZZ" type="Etc/GMT-14"/>
+		</mapTimezones>
+	</windowsZones>
+</supplementalData>
\ No newline at end of file
diff --git a/wrappers/python/CMakeLists.txt b/wrappers/python/CMakeLists.txt
index 008b33c3..b926b6a0 100644
--- a/wrappers/python/CMakeLists.txt
+++ b/wrappers/python/CMakeLists.txt
@@ -47,4 +47,5 @@ set_property(TARGET rego PROPERTY POSITION_INDEPENDENT_CODE ON)
 set_property(TARGET yaml PROPERTY POSITION_INDEPENDENT_CODE ON)
 set_property(TARGET json PROPERTY POSITION_INDEPENDENT_CODE ON)
 set_property(TARGET snmalloc-new-override PROPERTY POSITION_INDEPENDENT_CODE ON)
-set_property(TARGET re2 PROPERTY POSITION_INDEPENDENT_CODE ON)
\ No newline at end of file
+set_property(TARGET re2 PROPERTY POSITION_INDEPENDENT_CODE ON)
+set_property(TARGET date-tz PROPERTY POSITION_INDEPENDENT_CODE ON)
\ No newline at end of file
diff --git a/wrappers/python/README.md b/wrappers/python/README.md
index 73696cd2..a82be671 100644
--- a/wrappers/python/README.md
+++ b/wrappers/python/README.md
@@ -85,6 +85,8 @@ print(rego.query("x=[data.one, input.b, data.objects.sites[1]]"))
 
 ## Language Support
 
+We support v0.68.0 of Rego as defined by OPA, with the following grammar:
+
 ```ebnf
 module          = package { import } policy
 package         = "package" ref
@@ -157,8 +159,7 @@ LF     Line Feed
 
 ### Builtins
 
-At the moment only support a few builtins, but are actively working on adding
-all the standard builtins. The following builtins are currently supported:
+At the moment support the following builtins are available:
 
 - `aggregates`
 - `arrays`
@@ -172,12 +173,13 @@ all the standard builtins. The following builtins are currently supported:
 - `semver`
 - `sets`
 - `strings`
+- `time`
 - `types`
 - `units`
+- `uuid`
 - miscellaneous
     * `opa.runtime`
     * `print`
-    * `time.now_ns`
 
 ### Compatibility with the OPA Rego Go implementation
 
@@ -200,5 +202,3 @@ At present, we are **NOT** passing the following test suites in full:
 - `net*`
 - `planner-ir`
 - `providers-aws`
-- `time`
-- `walkbuiltin`
diff --git a/wrappers/python/docs/source/conf.py b/wrappers/python/docs/source/conf.py
index f2ad4049..028471d0 100644
--- a/wrappers/python/docs/source/conf.py
+++ b/wrappers/python/docs/source/conf.py
@@ -9,7 +9,7 @@
 project = 'regopy'
 copyright = '2024, Microsoft'
 author = 'Microsoft'
-release = '0.4.0'
+release = '0.4.4'
 
 # -- General configuration ---------------------------------------------------
 # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
diff --git a/wrappers/rust/regorust/Cargo.toml b/wrappers/rust/regorust/Cargo.toml
index b713a3f3..202a96b9 100644
--- a/wrappers/rust/regorust/Cargo.toml
+++ b/wrappers/rust/regorust/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "regorust"
-version = "0.4.0"
+version = "0.4.4"
 edition = "2021"
 description = "Rust bindings for the rego-cpp Rego compiler and interpreter"
 license = "MIT"
diff --git a/wrappers/rust/regorust/README.md b/wrappers/rust/regorust/README.md
index 3e803a1d..acb0623a 100644
--- a/wrappers/rust/regorust/README.md
+++ b/wrappers/rust/regorust/README.md
@@ -128,7 +128,7 @@ fn main() {
 
 ## Language Support
 
-At present we support v0.65.0 of Rego as defined by OPA, with the following grammar:
+We support v0.68.0 of Rego as defined by OPA, with the following grammar:
 
 ```ebnf
 module          = package { import } policy
@@ -202,8 +202,7 @@ LF     Line Feed
 
 ### Builtins
 
-At the moment only support a few builtins, but are actively working on adding
-all the standard builtins. The following builtins are currently supported:
+At the moment support the following builtins are available:
 
 - `aggregates`
 - `arrays`
@@ -217,12 +216,13 @@ all the standard builtins. The following builtins are currently supported:
 - `semver`
 - `sets`
 - `strings`
+- `time`
 - `types`
 - `units`
+- `uuid`
 - miscellaneous
     * `opa.runtime`
     * `print`
-    * `time.now_ns`
 
 ### Compatibility with the OPA Rego Go implementation
 
@@ -245,5 +245,3 @@ At present, we are **NOT** passing the following test suites in full:
 - `net*`
 - `planner-ir`
 - `providers-aws`
-- `time`
-- `walkbuiltin`
diff --git a/wrappers/rust/regorust/build.rs b/wrappers/rust/regorust/build.rs
index cd7d08e6..197fb438 100644
--- a/wrappers/rust/regorust/build.rs
+++ b/wrappers/rust/regorust/build.rs
@@ -86,8 +86,10 @@ fn main() {
     println!("cargo:rustc-link-lib=static=rego");
     println!("cargo:rustc-link-lib=static=json");
     println!("cargo:rustc-link-lib=static=yaml");
+    println!("cargo:rustc-link-lib=static=date-tz");
     if cfg!(windows) {
         println!("cargo:rustc-link-arg=mincore.lib");
+        println!("cargo:rustc-link-arg=shell32.lib");
     } else if cfg!(target_os = "macos") {
         println!("cargo:rustc-link-lib=c++");
     } else {


Test patch:
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 38ee2e81..39223b5c 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -111,6 +111,7 @@ if(REGOCPP_OPA_TESTS)
   sprintf
   strings
   subset
+  time
   topdowndynamicdispatch
   trim
   trimleft
@@ -125,8 +126,10 @@ if(REGOCPP_OPA_TESTS)
   union
   units
   urlbuiltins
+  uuid
   varreferences
   virtualdocs
+  walkbuiltin
   withkeyword
   )
 
diff --git a/tests/regocpp.yaml b/tests/regocpp.yaml
index ebb8291c..9d7cec66 100644
--- a/tests/regocpp.yaml
+++ b/tests/regocpp.yaml
@@ -1043,11 +1043,21 @@ cases:
         item = [n, l]
         item[0] == 3
     }
-  query: data.enumerate.a = x
+
+    b[[i, x]] {
+        some i, x in numbers
+        x > 2
+    }
+
+    output := [a, b]
+  query: data.enumerate.output = x
   want_result:
     - x:
-      - 3
-      - "3"
+        - [3, "3"]
+        -
+          - [2, 3]
+          - [3, 4]
+          - [4, 5]
 - note: regocpp/refrules
   modules:
   - |
@@ -1130,3 +1140,23 @@ cases:
     - x:
       - true
       - false
+- note: regocpp/uuid
+  modules:
+  - |
+    package test
+    import rego.v1
+
+    a := uuid.rfc4122("x")
+    b := uuid.rfc4122("x")
+    c := uuid.rfc4122("y")
+    d := uuid.parse(a)
+    output if {
+      d.version == 4
+      d.variant == "RFC4122"
+      a == b
+      a != c
+    }
+  query: data.test.output = x
+  want_result:
+    - x: true
+  strict_error: true
diff --git a/tests/test_case.cc b/tests/test_case.cc
index b63a8b1b..d7e20047 100644
--- a/tests/test_case.cc
+++ b/tests/test_case.cc
@@ -5,6 +5,70 @@
 #include "trieste/yaml.h"
 
 #include <stdexcept>
+#include <thread>
+
+namespace
+{
+  using namespace rego;
+
+  const std::size_t second_ns = 1000000000UL;
+  const std::size_t minute_ns = 60UL * second_ns;
+  const std::size_t hour_ns = 60UL * minute_ns;
+
+  const std::map<std::string, double> duration_units = {
+    {"ns", 1},
+    {"us", 1000},
+    {"µs", 1000},
+    {"ms", 1000000},
+    {"s", double(second_ns)},
+    {"m", double(minute_ns)},
+    {"h", double(hour_ns)}};
+
+  std::chrono::nanoseconds parse_duration(const std::string& duration)
+  {
+    const char* duration_re =
+      R"((-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][-+]?[0-9]+)?)((?:ns|us|µs|ms|s|m|h)))";
+    const RE2 re(duration_re);
+    assert(re.ok());
+
+    std::string number;
+    std::string unit;
+    std::int64_t ns = 0;
+    std::size_t start = 0;
+    while (start < duration.size())
+    {
+      re2::StringPiece input(duration.c_str() + start, duration.size() - start);
+      if (RE2::PartialMatch(input, re, &number, &unit))
+      {
+        double number_d = std::stod(number);
+        double unit_ns = duration_units.at(unit);
+        ns += std::int64_t(number_d * unit_ns);
+        start += number.size() + unit.size();
+      }
+      else
+      {
+        break;
+      }
+    }
+
+    return std::chrono::nanoseconds(ns);
+  }
+
+  Node test_sleep(const Nodes& args)
+  {
+    Node duration =
+      unwrap_arg(args, UnwrapOpt(0).type(JSONString).func("test.sleep"));
+    if (duration == Error)
+    {
+      return duration;
+    }
+
+    std::chrono::nanoseconds ns = parse_duration(get_string(duration));
+    std::this_thread::sleep_for(ns);
+
+    return Term << (Scalar << (True ^ "true"));
+  }
+}
 
 namespace rego_test
 {
@@ -592,6 +656,8 @@ namespace rego_test
   {
     rego::Interpreter interpreter(v1_compatible);
     interpreter.builtins().strict_errors(m_strict_error);
+    interpreter.builtins().register_builtin(
+      BuiltInDef::create(Location("test.sleep"), 1, test_sleep));
     interpreter.wf_check_enabled(wf_checks)
       .debug_enabled(!debug_path.empty())
       .debug_path(debug_path);