henninghall / react-native-date-picker

React Native Date Picker is datetime picker for Android and iOS. It includes date, time and datetime picker modes. The datepicker is customizable and is supporting different languages. It's written with native code to achieve the best possible look, feel and performance.
MIT License
2.23k stars 342 forks source link

Question: how to select in mode='time' a minimum before midnight and a maximum after midnight #444

Closed ThomasStubbe closed 2 years ago

ThomasStubbe commented 2 years ago

Describe the bug I want to configure a time-input going from 16:00 till 4:00 after midnight:

let minDate: Date = new Date();
minDate.setDate(minDate.getDate() -1) //Subtract one day (without this, the date input flashes completely jumping from min to max)
minDate.setHours(16, 0, 0);
let maxDate: Date = new Date();
maxDate.setHours(4, 0, 0);
<DatePicker style={styles.clockView}
                        mode='time'
                        minimumDate={minDate}
                        maximumDate={maxDate}/>

With this implementation I can select hours from 16:00h till 23:59. When selecting > 00:00, it will automatically jump to 16:00. Date is not relevant for me. I only want to set a time-window for the time that a person goes to bed. I'm probably missing something really basic.

Expected behavior I can select a time between 16:00 and 04:00

To Reproduce Add example code that reproduces the behavior.

export default class App extends Component {

  state = { 
    date: new Date() 
  }

  render () {
    let minDate: Date = new Date();
    minDate.setDate(minDate.getDate() -1);
    minDate.setHours(16, 0, 0);
    let maxDate: Date = new Date();
    maxDate.setHours(4, 0, 0);
    return (
      <DatePicker
        date={this.state.date}
        minimumDate={minDate}
        maximumDate={maxDate}
        onDateChange={date => this.setState({ date })}
      />
    );
  }
}

Smartphone (please complete the following information):

VolkerLieber commented 2 years ago

You can probably solve your issue like this:

<DatePicker
    mode="time"
    date={this.state.date}
    onDateChange={(date) => {
        // TODO: Check if the date is between 04:00 and 16:00 and correct it with date,setHours
        this.setState({ date: date });
    }}
/>
ThomasStubbe commented 2 years ago

Thanks for the suggestion. Like you suggested, I implemented my own min-max logic like this right now:

    const {minimumValue, maximumValue, defaultValue} = this.props.userInput;
    //Reset the date if it is drifting off, so we never double correct:
    if (!isNil(defaultValue) && defaultValue.getDay() !== value.getDay() && !isNil(minimumValue) && minimumValue.getDay() !== value.getDay() && !isNil(maximumValue) && maximumValue.getDay() !== value.getDay()) {
      value.setDate(defaultValue.getDate());
    }
    //Correct the date if crossing midnight (both backwards and forwards)
    if (!isNil(minimumValue) && !isNil(maximumValue)) {
      let tempDatePlus1 = new Date(value.getTime());
      tempDatePlus1.setDate(value.getDate() +1);
      let tempDateMin1 = new Date(value.getTime());
      tempDateMin1.setDate(value.getDate() -1);
      if (value < minimumValue && tempDatePlus1 < maximumValue) {
        value = new Date(tempDatePlus1.getTime());
      }
      if (value > maximumValue && tempDateMin1 > minimumValue) {
        value = new Date(tempDateMin1.getTime());
      }
    }
    if (!isNil(minimumValue) && value < minimumValue) {
      value = minimumValue;
    }
    if (!isNil(maximumValue) && value > maximumValue) {
      value = maximumValue;
    }
    //Update the randomKey so the date-component is redrawn (since the user input is out of sync with the state)
    const now: Date = new Date();
    this.setState({userInputValue: value, randomKey: now.getMilliseconds()});

This works. However, I noticed a second issue with this library: this solution will only correct the time a first time (e.g. choosing 15h when the max is 14h => it will go back to 14h), however choosing a time > 14h again will not update the component, because the state actually did not change (it stays 14h). A this.forceUpdate() also doesn't work. So basically the component does not know that it is displaying a different value than what the state is saying.

So I added a key to DatePicker: key={this.state.randomKey}, where randomKey is the getMilliseconds() of a new date...

This is a REALY ugly implementation to correct two different issues in the library, but it works

henninghall commented 2 years ago

Hi! I've investigated this issue a bit. I understand your problem. I've tried the iOS picker and it seems to have the same or a similar issue. Since the iOS picker is built on the underlaying native component UIDatePicker I don't really see a way to solve this properly. Even if it would be possible to fix on Android the issue would still persist on iOS.

Unfortunately, I think this has to be considered a limitation of this library, at least until it is supported in UIDatePicker

This workaround would be a custom validation logic, something similar to what you've written.