Repository: python-visualization/folium

Issue:
Format time to show year only in choropleth time slider
There may already be a way to do this, but I was not able to stumble on it...

I would like to be able to control the data format in the slider bar in a time slider choropleth.  In my case (and I believe many others), I am describing annual trends and all that is required in the slider is the year.

I am requesting:
1) Ability to set the time format displayed in the choropleth time slider itself by passing a date format. or setting a "show year only" variable, or if this is already present,
2) Enhanced documentation describing how this can be modified.


Discussion:

Patch:
diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml
index fc6a3854..2b0ee123 100644
--- a/.github/workflows/deploy-docs.yml
+++ b/.github/workflows/deploy-docs.yml
@@ -25,7 +25,7 @@ jobs:
         ref: main
 
     - name: Setup Micromamba env
-      uses: mamba-org/setup-micromamba@v1
+      uses: mamba-org/setup-micromamba@v2
       with:
         environment-name: TEST
         create-args: >-
diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml
index 86a63840..03165ec1 100644
--- a/.github/workflows/pypi.yml
+++ b/.github/workflows/pypi.yml
@@ -46,7 +46,7 @@ jobs:
 
     - name: Publish a Python distribution to PyPI
       if: success() && github.event_name == 'release'
-      uses: pypa/gh-action-pypi-publish@v1.10.2
+      uses: pypa/gh-action-pypi-publish@v1.10.3
       with:
         user: __token__
         password: ${{ secrets.PYPI_PASSWORD }}
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index a494c2a1..c3653127 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -2,7 +2,7 @@ exclude: examples/data/|.*\.css|.*\.json|.*\.geojson|.*\.html
 
 repos:
 - repo: https://github.com/pre-commit/pre-commit-hooks
-  rev: v4.6.0
+  rev: v5.0.0
   hooks:
     - id: trailing-whitespace
     - id: check-ast
@@ -15,7 +15,7 @@ repos:
       files: requirements-dev.txt
 
 - repo: https://github.com/astral-sh/ruff-pre-commit
-  rev: v0.6.3
+  rev: v0.6.9
   hooks:
     - id: ruff
 
diff --git a/folium/plugins/heat_map_withtime.py b/folium/plugins/heat_map_withtime.py
index 9a556773..11f8ee9b 100644
--- a/folium/plugins/heat_map_withtime.py
+++ b/folium/plugins/heat_map_withtime.py
@@ -86,7 +86,7 @@ class HeatMapWithTime(JSCSSMixin, Layer):
                 speedStep: {{this.speed_step}},
                 styleNS: "{{this.style_NS}}",
                 timeSlider: {{this.time_slider}},
-                timeSliderDrapUpdate: {{this.time_slider_drap_update}},
+                timeSliderDragUpdate: {{this.time_slider_drag_update}},
                 timeSteps: {{this.index_steps}}
                 })
                 .addTo({{this._parent.get_name()}});
@@ -199,7 +199,7 @@ class HeatMapWithTime(JSCSSMixin, Layer):
         self.time_slider = "true"
         self.play_button = "true"
         self.play_reverse_button = "true"
-        self.time_slider_drap_update = "false"
+        self.time_slider_drag_update = "false"
         self.style_NS = "leaflet-control-timecontrol"
 
     def render(self, **kwargs):
diff --git a/folium/plugins/time_slider_choropleth.py b/folium/plugins/time_slider_choropleth.py
index 9065af0e..ce1367d3 100644
--- a/folium/plugins/time_slider_choropleth.py
+++ b/folium/plugins/time_slider_choropleth.py
@@ -19,6 +19,8 @@ class TimeSliderChoropleth(JSCSSMixin, Layer):
     styledict: dict
         A dictionary where the keys are the geojson feature ids and the values are
         dicts of `{time: style_options_dict}`
+    date_options: str, default "ddd MMM DD YYYY"
+        A format string to render the currently active time in the control.
     highlight: bool, default False
         Whether to show a visual effect on mouse hover and click.
     name : string, default None
@@ -44,6 +46,11 @@ class TimeSliderChoropleth(JSCSSMixin, Layer):
             let styledict = {{ this.styledict|tojson }};
             let current_timestamp = timestamps[{{ this.init_timestamp }}];
 
+            function formatDate(date) {
+               var newdate = new moment(date);
+               return newdate.format({{this.date_format|tojson}});
+            }
+
             let slider_body = d3.select("body").insert("div", "div.folium-map")
                 .attr("id", "slider_{{ this.get_name() }}");
             $("#slider_{{ this.get_name() }}").hide();
@@ -64,7 +71,7 @@ class TimeSliderChoropleth(JSCSSMixin, Layer):
                 .attr("step", "1")
                 .style('align', 'center');
 
-            let datestring = new Date(parseInt(current_timestamp)*1000).toDateString();
+            let datestring = formatDate(parseInt(current_timestamp)*1000);
             d3.select("#slider_{{ this.get_name() }} > output").text(datestring);
 
             let fill_map = function(){
@@ -84,7 +91,7 @@ class TimeSliderChoropleth(JSCSSMixin, Layer):
 
             d3.select("#slider_{{ this.get_name() }} > input").on("input", function() {
                 current_timestamp = timestamps[this.value];
-                var datestring = new Date(parseInt(current_timestamp)*1000).toDateString();
+                let datestring = formatDate(parseInt(current_timestamp)*1000);
                 d3.select("#slider_{{ this.get_name() }} > output").text(datestring);
                 fill_map();
             });
@@ -155,12 +162,19 @@ class TimeSliderChoropleth(JSCSSMixin, Layer):
         """
     )
 
-    default_js = [("d3v4", "https://d3js.org/d3.v4.min.js")]
+    default_js = [
+        ("d3v4", "https://d3js.org/d3.v4.min.js"),
+        (
+            "moment",
+            "https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js",
+        ),
+    ]
 
     def __init__(
         self,
         data,
         styledict,
+        date_options: str = "ddd MMM DD YYYY",
         highlight: bool = False,
         name=None,
         overlay=True,
@@ -173,6 +187,7 @@ class TimeSliderChoropleth(JSCSSMixin, Layer):
     ):
         super().__init__(name=name, overlay=overlay, control=control, show=show)
         self.data = GeoJson.process_data(GeoJson({}), data)
+        self.date_format = date_options
         self.highlight = highlight
 
         self.stroke_opacity = stroke_opacity
@@ -180,14 +195,13 @@ class TimeSliderChoropleth(JSCSSMixin, Layer):
         self.stroke_color = stroke_color
 
         if not isinstance(styledict, dict):
-            raise ValueError(
-                f"styledict must be a dictionary, got {styledict!r}"
-            )  # noqa
+            raise ValueError(f"styledict must be a dictionary, got {styledict!r}")
+
         for val in styledict.values():
             if not isinstance(val, dict):
                 raise ValueError(
                     f"Each item in styledict must be a dictionary, got {val!r}"
-                )  # noqa
+                )
 
         # Make set of timestamps.
         timestamps_set = set()


Test patch:
diff --git a/.github/workflows/test_code.yml b/.github/workflows/test_code.yml
index dab203b4..37f103be 100644
--- a/.github/workflows/test_code.yml
+++ b/.github/workflows/test_code.yml
@@ -27,7 +27,7 @@ jobs:
     - uses: actions/checkout@v4
 
     - name: Setup Micromamba env
-      uses: mamba-org/setup-micromamba@v1
+      uses: mamba-org/setup-micromamba@v2
       with:
         environment-name: TEST
         create-args: >-
diff --git a/.github/workflows/test_latest_branca.yml b/.github/workflows/test_latest_branca.yml
index 598452dc..73b02540 100644
--- a/.github/workflows/test_latest_branca.yml
+++ b/.github/workflows/test_latest_branca.yml
@@ -14,7 +14,7 @@ jobs:
     - uses: actions/checkout@v4
 
     - name: Setup Micromamba env
-      uses: mamba-org/setup-micromamba@v1
+      uses: mamba-org/setup-micromamba@v2
       with:
         environment-name: TEST
         create-args: >-
diff --git a/.github/workflows/test_mypy.yml b/.github/workflows/test_mypy.yml
index 125b61d5..e99fb75c 100644
--- a/.github/workflows/test_mypy.yml
+++ b/.github/workflows/test_mypy.yml
@@ -14,7 +14,7 @@ jobs:
     - uses: actions/checkout@v4
 
     - name: Setup Micromamba env
-      uses: mamba-org/setup-micromamba@v1
+      uses: mamba-org/setup-micromamba@v2
       with:
         environment-name: TEST
         create-args: >-
diff --git a/.github/workflows/test_selenium.yml b/.github/workflows/test_selenium.yml
index 5c2f1aa1..e0ec1efd 100644
--- a/.github/workflows/test_selenium.yml
+++ b/.github/workflows/test_selenium.yml
@@ -18,7 +18,7 @@ jobs:
     - uses: actions/checkout@v4
 
     - name: Setup Micromamba env
-      uses: mamba-org/setup-micromamba@v1
+      uses: mamba-org/setup-micromamba@v2
       with:
         environment-name: TEST
         create-args: >-
diff --git a/tests/plugins/test_heat_map_withtime.py b/tests/plugins/test_heat_map_withtime.py
index 7713ec4e..ade6b4e6 100644
--- a/tests/plugins/test_heat_map_withtime.py
+++ b/tests/plugins/test_heat_map_withtime.py
@@ -59,7 +59,7 @@ def test_heat_map_with_time():
             speedStep: {{this.speed_step}},
             styleNS: "{{this.style_NS}}",
             timeSlider: {{this.time_slider}},
-            timeSliderDrapUpdate: {{this.time_slider_drap_update}},
+            timeSliderDragUpdate: {{this.time_slider_drag_update}},
             timeSteps: {{this.index_steps}}
             })
             .addTo({{this._parent.get_name()}});