Repository: streamlit/streamlit

Issue:
Datetime slider showing incorrect values on the browser
### Summary

When I run my streamlit app at my localhost and the cloud, I see different st.slider time range values. To be precise the range values printed on the browser seem to be shifted. I suspect this shift is due to some time zone change.

### Code snippet:

```
from datetime import time
import streamlist as st

period = st.sidebar.slider("Period:", min_value=time(0,0), max_value=time(23, 45), value=(time(10, 00), time(23, 45)))
```

The code above shows a time period of 00:00 - 23:45 at my localhost, and 19:00 - 18:45 on the cloud with the same time length.

### Debug info

- Streamlit version: 0.82.0

### Suggestion

I think st.slider should have a timezone keyword and in default should return the datetime information in users timezone rather than the server's timezone.


Discussion:
Hey @tdincer ! Thanks for the report! We have had a couple people mention our support for timezones is lacking. I have grouped this in with other issues. Most similar is #1346. We're going to get this in front of our product team to see how we can best integrate this with our future plans.

Resolving patch:
diff --git a/lib/streamlit/elements/slider.py b/lib/streamlit/elements/slider.py
--- a/lib/streamlit/elements/slider.py
+++ b/lib/streamlit/elements/slider.py
@@ -405,8 +405,11 @@ def _delta_to_micros(delta):
             UTC_EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc)
 
             def _datetime_to_micros(dt):
-                # If dt is naive, Python converts from local time
-                utc_dt = dt.astimezone(timezone.utc)
+                # The frontend is not aware of timezones and only expects a UTC-based timestamp (in microseconds).
+                # Since we want to show the date/time exactly as it is in the given datetime object,
+                # we just set the tzinfo to UTC and do not do any timezone conversions.
+                # Only the backend knows about original timezone and will replace the UTC timestamp in the deserialization.
+                utc_dt = dt.replace(tzinfo=timezone.utc)
                 return _delta_to_micros(utc_dt - UTC_EPOCH)
 
             # Restore times/datetimes to original timezone (dates are always naive)
@@ -418,8 +421,9 @@ def _datetime_to_micros(dt):
 
             def _micros_to_datetime(micros):
                 utc_dt = UTC_EPOCH + timedelta(microseconds=micros)
-                # Convert from utc back to original time (local time if naive)
-                return utc_dt.astimezone(orig_tz).replace(tzinfo=orig_tz)
+                # Add the original timezone. No conversion is required here,
+                # since in the serialization, we also just replace the timestamp with UTC.
+                return utc_dt.replace(tzinfo=orig_tz)
 
             value = list(map(_datetime_to_micros, value))
             min_value = _datetime_to_micros(min_value)


Test patch:
diff --git a/lib/tests/streamlit/slider_test.py b/lib/tests/streamlit/slider_test.py
--- a/lib/tests/streamlit/slider_test.py
+++ b/lib/tests/streamlit/slider_test.py
@@ -55,10 +55,10 @@ def test_just_disabled(self):
     AWARE_TIME = time(12, 00, tzinfo=PST)
     AWARE_TIME_END = time(21, 00, tzinfo=PST)
     # datetimes are serialized in proto as micros since epoch
-    AWARE_DT_MICROS = 1577865600000000
-    AWARE_DT_END_MICROS = 1578211200000000
-    AWARE_TIME_MICROS = 946756800000000
-    AWARE_TIME_END_MICROS = 946789200000000
+    AWARE_DT_MICROS = 1577836800000000
+    AWARE_DT_END_MICROS = 1578182400000000
+    AWARE_TIME_MICROS = 946728000000000
+    AWARE_TIME_END_MICROS = 946760400000000
 
     @parameterized.expand(
         [