Skip to content

Commit

Permalink
fix(android): min/max dates not always correct when `timezoneOffsetIn…
Browse files Browse the repository at this point in the history
…Minutes` is set (#635)
  • Loading branch information
henninghall committed Apr 18, 2023
1 parent 3799a37 commit 40e74b2
Show file tree
Hide file tree
Showing 12 changed files with 85 additions and 57 deletions.
27 changes: 25 additions & 2 deletions .maestro/timezone-offset-in-minutes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ appId: com.rn069
- runFlow:
file: utils/set-timezone.yml
env:
TIMEZONE: Sweden
REGION: Sweden
GMT: GMT+01:00
STATE: ''

- runFlow: utils/launch.yml

Expand Down Expand Up @@ -53,8 +54,9 @@ appId: com.rn069
- runFlow:
file: utils/set-timezone.yml
env:
TIMEZONE: Sweden
REGION: Sweden
GMT: GMT+02:00
STATE: ''

- runFlow: utils/launch.yml

Expand Down Expand Up @@ -118,3 +120,24 @@ appId: com.rn069
- assertVisible: 'Thu Jun 1101 AM '

- runFlow: utils/reset.yml

# test: timezoneOffsetInMinutes combined with maximumDate/minimumDate in another timezone than current device.
# Bug was reported here: https://github.com/henninghall/react-native-date-picker/issues/613
- runFlow:
file: utils/set-timezone.yml
env:
REGION: 'United states'
STATE: Phoenix
GMT: GMT-07:00
- runFlow: utils/launch.yml
- runFlow:
file: utils/change-prop.yml
env:
PROP: timeZoneOffsetInMinutes
VALUE: 180
- repeat:
times: 5
commands:
- runFlow: utils/swipe-wheel-1.yml
- runFlow: utils/swipe-wheel-4.yml
- assertVisible: 'Thu Jan 61000 AM '
12 changes: 11 additions & 1 deletion .maestro/utils/select-region.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
appId: com.android.settings
---
- tapOn: Region
- inputText: ${TIMEZONE}
- inputText: ${REGION}
- tapOn:
text: ${REGION}
index: 1

- runFlow:
when:
true: ${REGION == 'United states'}
file: tap.yml
env:
TEXT: ${STATE}
8 changes: 0 additions & 8 deletions .maestro/utils/set-timezone-us.yml

This file was deleted.

3 changes: 2 additions & 1 deletion .maestro/utils/set-timezone.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@ appId: com.android.settings
visible: Region
file: select-region.yml
env:
TIMEZONE: ${TIMEZONE}
REGION: ${REGION}
STATE: ${STATE}
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
package com.henninghall.date_picker;


import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;

import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.uimanager.SimpleViewManager;
Expand All @@ -29,7 +21,7 @@
import com.henninghall.date_picker.props.MinuteIntervalProp;
import com.henninghall.date_picker.props.ModeProp;
import com.henninghall.date_picker.props.TextColorProp;
import com.henninghall.date_picker.props.UtcProp;
import com.henninghall.date_picker.props.TimezoneOffsetInMinutesProp;


import java.lang.reflect.Method;
Expand All @@ -54,7 +46,7 @@ public PickerView createViewInstance(ThemedReactContext context) {
}

@ReactPropGroup(names = { DateProp.name, ModeProp.name, LocaleProp.name, MaximumDateProp.name,
MinimumDateProp.name, FadeToColorProp.name, TextColorProp.name, UtcProp.name, MinuteIntervalProp.name,
MinimumDateProp.name, FadeToColorProp.name, TextColorProp.name, TimezoneOffsetInMinutesProp.name, MinuteIntervalProp.name,
VariantProp.name, DividerHeightProp.name, Is24hourSourceProp.name
})
public void setProps(PickerView view, int index, Dynamic value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import com.henninghall.date_picker.props.MaximumDateProp;
import com.henninghall.date_picker.props.MinimumDateProp;
import com.henninghall.date_picker.props.MinuteIntervalProp;
import com.henninghall.date_picker.props.UtcProp;
import com.henninghall.date_picker.props.TimezoneOffsetInMinutesProp;
import com.henninghall.date_picker.props.VariantProp;
import com.henninghall.date_picker.props.DateProp;
import com.henninghall.date_picker.props.FadeToColorProp;
Expand Down Expand Up @@ -77,7 +77,7 @@ public void update() {

if (didUpdate(DateProp.name, HeightProp.name, LocaleProp.name,
MaximumDateProp.name, MinimumDateProp.name, MinuteIntervalProp.name, ModeProp.name,
UtcProp.name, VariantProp.name
TimezoneOffsetInMinutesProp.name, VariantProp.name
)) {
uiManager.updateDisplayValues();
}
Expand Down
25 changes: 16 additions & 9 deletions android/src/main/java/com/henninghall/date_picker/State.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
import com.henninghall.date_picker.props.ModeProp;
import com.henninghall.date_picker.props.Prop;
import com.henninghall.date_picker.props.TextColorProp;
import com.henninghall.date_picker.props.UtcProp;
import com.henninghall.date_picker.props.TimezoneOffsetInMinutesProp;

import net.time4j.tz.Timezone;

import java.util.Calendar;
import java.util.HashMap;
Expand All @@ -35,7 +37,7 @@ public class State {
private final MinuteIntervalProp minuteIntervalProp = new MinuteIntervalProp();
private final MinimumDateProp minimumDateProp = new MinimumDateProp();
private final MaximumDateProp maximumDateProp = new MaximumDateProp();
private final UtcProp utcProp = new UtcProp();
private final TimezoneOffsetInMinutesProp timezoneOffsetInMinutesProp = new TimezoneOffsetInMinutesProp();
private final HeightProp heightProp = new HeightProp();
private final VariantProp variantProp = new VariantProp();
private final DividerHeightProp dividerHeightProp = new DividerHeightProp();
Expand All @@ -50,7 +52,7 @@ public class State {
put(MinuteIntervalProp.name, minuteIntervalProp);
put(MinimumDateProp.name, minimumDateProp);
put(MaximumDateProp.name, maximumDateProp);
put(UtcProp.name, utcProp);
put(TimezoneOffsetInMinutesProp.name, timezoneOffsetInMinutesProp);
put(HeightProp.name, heightProp);
put(VariantProp.name, variantProp);
put(DividerHeightProp.name, dividerHeightProp);
Expand Down Expand Up @@ -91,18 +93,23 @@ public Locale getLocale() {
}

public Calendar getMinimumDate() {
DateBoundary db = new DateBoundary(getTimeZone(), (String) minimumDateProp.getValue());
return db.get();
return Utils.isoToCalendar(minimumDateProp.getValue(), getTimeZone());
}

public Calendar getMaximumDate() {
DateBoundary db = new DateBoundary(getTimeZone(), (String) maximumDateProp.getValue());
return db.get();
return Utils.isoToCalendar(maximumDateProp.getValue(), getTimeZone());
}

public TimeZone getTimeZone() {
boolean utc = (boolean) utcProp.getValue();
return utc ? TimeZone.getTimeZone("UTC") : TimeZone.getDefault();
Integer offset = timezoneOffsetInMinutesProp.getValue();
if(offset == null) return TimeZone.getDefault();
int totalOffsetMinutes = Math.abs(offset);
char offsetDirection = offset < 0 ? '-' : '+';
int offsetHours = (int) Math.floor(totalOffsetMinutes / 60f);
int offsetMinutes = totalOffsetMinutes - offsetHours * 60;
String timeZoneId = "GMT" + offsetDirection + offsetHours + ":" + Utils.toPaddedMinutes(offsetMinutes);
TimeZone zone = TimeZone.getTimeZone(timeZoneId);
return zone;
}

public String getIsoDate() {
Expand Down
6 changes: 6 additions & 0 deletions android/src/main/java/com/henninghall/date_picker/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import net.time4j.PrettyTime;

import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
Expand Down Expand Up @@ -110,4 +111,9 @@ public static String getLocalisedStringFromResources(Locale locale, String tagNa
public static int toDp(int pixels){
return (int) (pixels * DatePickerPackage.context.getResources().getDisplayMetrics().density);
}

public static String toPaddedMinutes(int minutes){
DecimalFormat df = new DecimalFormat("00");
return df.format(minutes);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.henninghall.date_picker.props;

import com.facebook.react.bridge.Dynamic;

public class TimezoneOffsetInMinutesProp extends Prop<Integer> {
public static final String name = "timezoneOffsetInMinutes";

@Override
Integer toValue(Dynamic value) {
if(value.isNull()) return null;
return value.asInt();
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,8 @@ private Calendar getStartCal(){
Calendar min = state.getMinimumDate();
if (min != null) {
cal = (Calendar) min.clone();
resetToMidnight(cal);
} else if (max != null) {
cal = (Calendar) max.clone();
resetToMidnight(cal);
cal.add(Calendar.DATE, -cal.getActualMaximum(Calendar.DAY_OF_YEAR) / 2);
} else {
cal = (Calendar) getInitialDate().clone();
Expand All @@ -68,10 +66,8 @@ private Calendar getEndCal(){
Calendar min = state.getMinimumDate();
if (max != null) {
cal = (Calendar) max.clone();
resetToMidnight(cal);
} else if (min != null) {
cal = (Calendar) min.clone();
resetToMidnight(cal);
cal.add(Calendar.DATE, cal.getActualMaximum(Calendar.DAY_OF_YEAR) / 2);
} else {
cal = (Calendar) getInitialDate().clone();
Expand Down
16 changes: 8 additions & 8 deletions src/DatePickerAndroid.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,15 @@ class DatePickerAndroid extends React.PureComponent {
date: this._date(),
minimumDate: this._minimumDate(),
maximumDate: this._maximumDate(),
utc: this.props.timeZoneOffsetInMinutes !== undefined,
timezoneOffsetInMinutes: this._getTimezoneOffsetInMinutes(),
style: this._getStyle(),
})

_getTimezoneOffsetInMinutes = () => {
if (this.props.timeZoneOffsetInMinutes == undefined) return undefined
return this.props.timeZoneOffsetInMinutes
}

_getStyle = () => {
const width = this.props.mode === 'time' ? timeModeWidth : defaultWidth
return [{ width, height }, this.props.style]
Expand All @@ -74,16 +79,11 @@ class DatePickerAndroid extends React.PureComponent {
_date = () => this._toIsoWithTimeZoneOffset(this.props.date)

_fromIsoWithTimeZoneOffset = (timestamp) => {
const date = new Date(timestamp)
if (this.props.timeZoneOffsetInMinutes === undefined) return date
return addMinutes(date, -this.props.timeZoneOffsetInMinutes)
return new Date(timestamp)
}

_toIsoWithTimeZoneOffset = (date) => {
if (this.props.timeZoneOffsetInMinutes === undefined)
return date.toISOString()

return addMinutes(date, this.props.timeZoneOffsetInMinutes).toISOString()
return date.toISOString()
}

_onConfirm = (isoDate) => {
Expand Down

0 comments on commit 40e74b2

Please sign in to comment.