From bdf6179faedcec067692d29e5a33bb7ba421f307 Mon Sep 17 00:00:00 2001 From: Paito Anderson Date: Sun, 19 Dec 2021 16:48:46 -0500 Subject: [PATCH] fix: (android) fix issue with minimumDate/maximumDate when using timeZoneOffsetInMinutes (#519) * Fixes #518 * Add setTimeZone * Revert setting end of day * Add toggleMinMaxDate * Better Demo * Add E2E test for min/max * Fix Android test * Update android/src/main/java/com/reactcommunity/rndatetimepicker/RNDatePickerDialogFragment.java Co-authored-by: Vojtech Novak * Update example/App.js Co-authored-by: Vojtech Novak * More consistency * Fix ESLint error Co-authored-by: Vojtech Novak --- .../RNDatePickerDialogFragment.java | 27 +++++++++- example/App.js | 28 +++++++++- example/e2e/detoxTest.spec.js | 52 ++++++++++++++++++- 3 files changed, 103 insertions(+), 4 deletions(-) diff --git a/android/src/main/java/com/reactcommunity/rndatetimepicker/RNDatePickerDialogFragment.java b/android/src/main/java/com/reactcommunity/rndatetimepicker/RNDatePickerDialogFragment.java index 2a6ec0d6..f2e43063 100644 --- a/android/src/main/java/com/reactcommunity/rndatetimepicker/RNDatePickerDialogFragment.java +++ b/android/src/main/java/com/reactcommunity/rndatetimepicker/RNDatePickerDialogFragment.java @@ -25,6 +25,7 @@ import java.util.Calendar; import java.util.Locale; +import java.util.TimeZone; @SuppressLint("ValidFragment") public class RNDatePickerDialogFragment extends DialogFragment { @@ -108,6 +109,11 @@ static DatePickerDialog createDialog( final DatePicker datePicker = dialog.getDatePicker(); + Integer timeZoneOffsetInMilliseconds = getTimeZoneOffset(args); + if (timeZoneOffsetInMilliseconds != null) { + c.setTimeZone(TimeZone.getTimeZone("GMT")); + } + if (args != null && args.containsKey(RNConstants.ARG_MINDATE)) { // Set minDate to the beginning of the day. We need this because of clowniness in datepicker // that causes it to throw an exception if minDate is greater than the internal timestamp @@ -117,7 +123,7 @@ static DatePickerDialog createDialog( c.set(Calendar.MINUTE, 0); c.set(Calendar.SECOND, 0); c.set(Calendar.MILLISECOND, 0); - datePicker.setMinDate(c.getTimeInMillis()); + datePicker.setMinDate(c.getTimeInMillis() - getOffset(c, timeZoneOffsetInMilliseconds)); } else { // This is to work around a bug in DatePickerDialog where it doesn't display a title showing // the date under certain conditions. @@ -130,12 +136,29 @@ static DatePickerDialog createDialog( c.set(Calendar.MINUTE, 59); c.set(Calendar.SECOND, 59); c.set(Calendar.MILLISECOND, 999); - datePicker.setMaxDate(c.getTimeInMillis()); + datePicker.setMaxDate(c.getTimeInMillis() - getOffset(c, timeZoneOffsetInMilliseconds)); } return dialog; } + private static Integer getTimeZoneOffset(Bundle args) { + if (args != null && args.containsKey(RNConstants.ARG_TZOFFSET_MINS)) { + long timeZoneOffsetInMinutesFallback = args.getLong(RNConstants.ARG_TZOFFSET_MINS); + int timeZoneOffsetInMinutes = args.getInt(RNConstants.ARG_TZOFFSET_MINS, (int) timeZoneOffsetInMinutesFallback); + return timeZoneOffsetInMinutes * 60000; + } + + return null; + } + + private static int getOffset(Calendar c, Integer timeZoneOffsetInMilliseconds) { + if (timeZoneOffsetInMilliseconds != null) { + return TimeZone.getDefault().getOffset(c.getTimeInMillis()) - timeZoneOffsetInMilliseconds; + } + return 0; + } + @Override public void onDismiss(DialogInterface dialog) { super.onDismiss(dialog); diff --git a/example/App.js b/example/App.js index df702d7b..a67c18fa 100644 --- a/example/App.js +++ b/example/App.js @@ -62,7 +62,8 @@ const MINUTE_INTERVALS = [1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30]; export const App = () => { // Sat, 13 Nov 2021 10:00:00 GMT (local: Saturday, November 13, 2021 11:00:00 AM GMT+01:00) - const sourceDate = moment.unix(1636797600).local().toDate(); + const sourceMoment = moment.unix(1636797600); + const sourceDate = sourceMoment.local().toDate(); const [date, setDate] = useState(sourceDate); const [tzOffsetInMinutes, setTzOffsetInMinutes] = useState(undefined); const [mode, setMode] = useState(MODE_VALUES[0]); @@ -72,6 +73,8 @@ export const App = () => { const [interval, setMinInterval] = useState(1); const [neutralButtonLabel, setNeutralButtonLabel] = useState(undefined); const [disabled, setDisabled] = useState(false); + const [minimumDate, setMinimumDate] = useState(); + const [maximumDate, setMaximumDate] = useState(); // Windows-specific const [time, setTime] = useState(undefined); @@ -111,6 +114,17 @@ export const App = () => { backgroundColor: isDarkMode ? Colors.dark : Colors.lighter, }; + const toggleMinMaxDate = () => { + const startOfTodayUTC = sourceMoment.utc().startOf('day').toDate(); + setMinimumDate(maximumDate ? undefined : startOfTodayUTC); + const endOfTomorrowUTC = sourceMoment + .utc() + .endOf('day') + .add(1, 'day') + .toDate(); + setMaximumDate(minimumDate ? undefined : endOfTomorrowUTC); + }; + if (Platform.OS !== 'windows') { return ( @@ -267,11 +281,23 @@ export const App = () => { title="setTzOffsetInMinutes to 120" /> + +