I am looking to a solution for my ReactJS project.
In a property page I want to display a Date Range Picker with the availability of the property.
By running a query with Postman I get the availability from the calendar with the following format, although there is no problem for me to manipulate the array in order to fit the requirements for the date range picker.
How to display inside the date range picker the blocked days so they are not clickable. I already tried with the DateRangePicker of rsuite and storybook but no luck for me. For the rsuite the data should have the following format:
It will depend on the date range picker you decide to use (or if you write your own) - a quick look at this component's API, for example, will tell you that it has a disabledDates: Date[] prop that you can use to disable certain dates.
The way I managed to do it was to get the data from the API.
Manipulated and syntax in a way the DateRangePicker could import it via a function.
axios(config)
.then(function (response) {
//console.log(JSON.stringify(response.data));
calendar= response.data.calendar;
console.log(calendar);
var highlighted = [];
var disabled_days=[];
for(var i=0;i<calendar.length;i++) {
var item = calendar[i];
if(item.status === 'available') {
highlighted.push(new Date(item.date));
//console.log(item.date);
}
else{
disabled_days.push(new Date(item.date));
}
};
modifiers = {
disabled: disabled_days,
highlight: highlighted
}
self.setState({
modifiers: modifiers})
//console.log(modifiers);
})
.catch(function (error) {
console.log(error);
});
Then I used the package 'react-calendar' and import it to my node project.
npm i 'react-calendar' --save
Then I used the component Calendar as I have import it via the following command:
import Calendar from '../Components/CalendarRangePicker';
...
<Calendar modifiers={modifiers}/>
...
CalendarRangePicker.js
import { useState } from 'react';
import Calendar from 'react-calendar';
import 'react-calendar/dist/Calendar.css';
function CalendarRangePicker(props){
const [value, onChange] = useState(new Date());
const dataNotYetFetched = useState();
// disabled some days until I fetched the data...
var disabledDates = [
new Date(2021, 7, 1),
new Date(2021, 7, 2),
];
//console.log(disabledDates);
var modifiers = null;
if(props.modifiers != null) {
modifiers = props.modifiers;
console.log(modifiers);
disabledDates = modifiers.disabled;
}
return (
<div>
<Calendar
// Make calendar not viewable for previous months
minDate={new Date()}
// Whether to show two months
showDoubleView = {false}
ActiveStartDate = {new Date()}
returnValue={"range"}
// settings for the calendar
onChange={onChange}
value={value}
selectRange={true}
locale="en-US"
autofocus={false}
// disabled dates. Got data from channel manager
tileDisabled={({date, view}) =>
(view === 'month') && // Block day tiles only
disabledDates.some(disabledDate =>
date.getFullYear() === disabledDate.getFullYear() &&
date.getMonth() === disabledDate.getMonth() &&
date.getDate() === disabledDate.getDate()
)}
/>
</div>
);
}
export default CalendarRangePicker;
I am attempting to pull a string integer from my database, convert it into a number, then transform that number into a date.
However, I keep getting the following error every time I load my page when live.
Objects are not valid as a React child (found: Mon Dec 14 2020
10:48:11 GMT+0000 (GMT)). If you meant to render a collection of
children, use an array instead.
The date variables are working exactly as expected, I just want to now render them in my app, which is where the problem is occurring.
Can someone point out where I'm going wrong here?
Here's my full component....
import React, { Component } from 'react';
import axios from 'axios';
import './individual-debts.css';
import IndividualDebtCard from '../individual-debts/individual-debt-card';
class IndividualDebts extends Component {
constructor(props) {
super(props)
this.state = {
debts: []
}
}
componentDidMount() {
axios.get("/api/fetch/fetchDebtCards")
.then((response) => {
this.setState({
debts: response.data,
fetchInProgress: false
})
})
})
}
render() {
const fetchDebts = this.state.debts.map (debt => {
const dayCurrent = new Date();
const dayFromDB = parseInt(debt.date);
const dayFromDBDate = new Date(new Date().setDate(dayFromDB));
const variableDayFromDB = new Date(new Date().setDate(dayFromDB));
const dayFromDBDatePlusOne = variableDayFromDB.setMonth(variableDayFromDB.getMonth()+1)
let dayToRender = ''
if (dayCurrent < dayFromDBDate) {
dayToRender = dayFromDBDate
console.log(dayToRender)
} else {
dayToRender = variableDayFromDB
console.log(dayToRender)
}
return (
<IndividualDebtCard key={debt._id}
monthly={debt.monthly}
repayment={dayToRender} />
)
})
return (
<div>
{fetchDebts}
</div>
)
}
}
export default IndividualDebts;
Any advice here would be really appreciated! Thank you.
Date instance is an object in JavaScript so it cant be passed as a children prop in react (since it accepts only a string or an element)
To solve this issue simply convert the Date instance to an ISOString (or any other format you like)
<IndividualDebtCard
key={debt._id}
monthly={debt.monthly}
repayment={dayToRender.toISOString()} />
If you are trying to render your date in a <p> (for example), it won't work as dates are Objects.
You could write a function to transform it to a string or use a lib like date-fns.
Here is a go at it:
export const DisplayDate = (date: Date | string | number): string => {
if (date) {
const dateAsDate = new Date(date);
const day =
dateAsDate.getDate() < 10
? `0${dateAsDate.getDate()}`
: dateAsDate.getDate();
const month =
dateAsDate.getMonth() < 9
? `0${dateAsDate.getMonth() + 1}`
: `${dateAsDate.getMonth() + 1}`;
const year = dateAsDate.getFullYear();
return `${day}-${month}-${year}`;
}
return '-';
};
following code disabled all the previous dates including today, but I want to disable all the Sundays and array of specific days in ant design date picker.
< DatePicker size = "large"
format = "DD/MM/YYYY"
nChange = {
this.onDate1Change
}
disabledDate = {
current => {
return current && current < moment().endOf('day');
}
}
/>
To start we have a look at antd's Datepicker example in the depending docs.
Disable all Sundays
We use the moment.js lib to check the days and disable all sundays (here it is the first == zero).
E.g.:
function disabledDate(current) {
// Can not select sundays and predfined days
return moment(current).day() === 0
}
Disable specific days
First we define an array of our dates and then check if a converted day is in our disabled array.
const disabledDates = ["2020-07-21", "2020-07-23"];
function disabledDate(current) {
// Can not select Sundays and predefined days
return disabledDates.find(date => date === moment(current).format("YYYY-MM-DD"));
}
Putting all together
Now we can combine both solutions. A working example can be found in this CodeSandbox.
E.g.:
import React from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css";
import moment from "moment";
import { DatePicker } from "antd";
const disabledDates = ["2020-07-21", "2020-07-23"];
function disabledDate(current) {
// Can not select Sundays and predefined days
return (
moment(current).day() === 0 ||
disabledDates.find(date => date === moment(current).format("YYYY-MM-DD"))
);
}
ReactDOM.render(
<>
<DatePicker
format="YYYY-MM-DD HH:mm:ss"
disabledDate={disabledDate}
showTime={{ defaultValue: moment("00:00:00", "HH:mm:ss") }}
/>
</>,
document.getElementById("container")
);
If you don't want to use the "Moment" library, you can do as I did:
const disabledDate = (current) => {
return (
current < Date.now() ||
(new Date(current).getDay() === 0 ||
new Date(current).getDay() === 6)
);
};
I'm using the react-datepicker to let user select a date. However, right now it uses local time (PDT), but I want to hardcode it to use a specific timezone (PST).
I tried using utcOffset prop but it doesn't seem to be doing anything. Does anyone know how to accomplish this?
For my part I set the defaultTimezone before rendering the React-dates plugin.
React-dates will just use the default timezone.
moment.tz.setDefault('America/Los_Angeles');
This works for me:
import React, { ComponentProps } from "react"
import DatePicker from "react-datepicker"
import moment from "moment"
interface Props {
timezone: string
}
const DatePickerWithTimezone = ({
selected,
onChange,
timezone,
...props
}: Props & ComponentProps<typeof DatePicker>) => (
<DatePicker
selected={selected ? setLocalZone(selected, timezone) : null}
onChange={(v, e) => {
onChange(v ? setOtherZone(v, timezone) : null, e)
}}
{...props}
/>
)
const setLocalZone = (date: Date, timezone: string) => {
const dateWithoutZone = moment
.tz(date, timezone)
.format("YYYY-MM-DDTHH:mm:ss.SSS")
const localZone = moment(dateWithoutZone).format("Z")
const dateWithLocalZone = [dateWithoutZone, localZone].join("")
return new Date(dateWithLocalZone)
}
const setOtherZone = (date: Date, timezone: string) => {
const dateWithoutZone = moment(date).format("YYYY-MM-DDTHH:mm:ss.SSS")
const otherZone = moment.tz(date, timezone).format("Z")
const dateWithOtherZone = [dateWithoutZone, otherZone].join("")
return new Date(dateWithOtherZone)
}
export default DatePickerWithTimezone
since datepicker doesn't use moment.js anymore i tried to implement a hacky solution for this issue, assuming the initial value is a string for instance:
export const formatUTC = (dateInt, addOffset = false) => {
let date = (!dateInt || dateInt.length < 1) ? new Date : new Date(dateInt);
if (typeof dateInt === "string") {
return date;
} else {
const offset = addOffset ? date.getTimezoneOffset() : -(date.getTimezoneOffset());
const offsetDate = new Date();
offsetDate.setTime(date.getTime() + offset * 60000)
return offsetDate;
}
}
inside date i call the formatter like this:
selected={formatUTC(this.props.input.value,true)}
onChange={date => formatUTC(date)}
I've been thru this, If you decided that you want to just ignore your local offset then you can hardcode the zone.
Observation just to give a complete answer: PST will always be -08:00, but if you want for example pacific time, right now is -07:00, in this case, you may want to install 'moment.timezone' then import moment from 'moment-timezone' and just get the current offset with moment.tz('US/Pacific').format('Z')
The code in typescript (I can change it to Javascript if you want):
interface ICalendarInputProps {
handleChange: (newDate: moment.Moment) => void;
}
const CalendarInput = ({ handleChange }: ICalendarInputProps) => {
const onChange = (date: Date) => {
handleChange(moment(`${moment(date).format('YYYY-MM-DDThh:mm:ss')}-08:00`));
// This is to get the offset from a timezone: handleChange(moment(`${moment(date).format('YYYY-MM-DDThh:mm:ss')}${moment.tz('US/Pacific').format('Z')}`));
};
return (<DatePicker onChange={onChange} />);
};
export default CalendarInput;
This component outputs Date objects set to midnight local-time at the start of the chosen day. This is a problem. If there is a way of configuring this behaviour, I haven't found it.
The only way to stay sane when dealing with dates is to make sure that your dates are always midnight UTC at the start of the date in question. To get this behaviour from react-datepicker, the only thing I've found is to subtract the timezone offset from the output...
interface IProps {
value: any
setValue: (value: Date) => void
}
const DayPicker: FC<IProps> = ({ value, setValue, placeholderText = "", minDate = new Date() }) => {
function correctToUtcThenSet(val: Date) {
setValue(new Date(val.getTime() - val.getTimezoneOffset() * 60000))
}
return <DatePicker
onChange={correctToUtcThenSet}
selected={value}
/>
}
Other answers didn't work as I'd hoped, and sometimes the dates were off by 1 day because of time zone differences.
This is what I needed:
import DatePicker from 'react-datepicker';
import 'react-datepicker/dist/react-datepicker.css';
import { getEndOfDayUtc, treatLocalDateAsUtcMidnight, treatUtcMidnightAsLocalDate } from '../../../shared/helpers/datetime';
type DatePickerUtcProps = {
selected: Date | string;
onChange: any;
isEndOfDay: boolean;
};
function getSelectedAsLocal(selected: Date | string): Date {
const selectedDate = typeof selected === 'string' ? new Date(selected) : selected;
return treatUtcMidnightAsLocalDate(selectedDate);
}
export function DatePickerUtc({ selected, onChange, isEndOfDay, ...datePickerProps }: DatePickerUtcProps): JSX.Element {
function onChangeAsUtc(local: Date) {
const utc = treatLocalDateAsUtcMidnight(local);
const adjusted = isEndOfDay ? getEndOfDayUtc(utc) : utc;
console.log('onChangeAsUtc', { local, utc, adjusted, isEndOfDay });
onChange(adjusted);
}
return <DatePicker onChange={onChangeAsUtc} selected={getSelectedAsLocal(selected)} {...datePickerProps} />;
}
export function treatLocalDateAsUtcMidnight(localDate: Date): Date {
const moment = dayjs(localDate).tz('UTC', true); // https://day.js.org/docs/en/plugin/timezone
const utcMidnight = getStartOfDayUtc(moment.toDate());
console.log({ localDate, utcMidnight });
return utcMidnight;
}
export function treatUtcMidnightAsLocalDate(utcMidnight: Date): Date {
const sliceOfJustTheDatePart = utcMidnight.toISOString().slice(0, 10);
const localDate = dayjs(sliceOfJustTheDatePart).toDate();
console.log({ localDate, sliceOfJustTheDatePart, utcMidnight });
return localDate;
}
From: <DatePickerUtc selected={startDate} onChange={(utcDate: Date) => setStartDate(utcDate)} {...datePickerProps} />
To: <DatePickerUtc selected={endDate} onChange={(utcDate: Date) => setEndDate(utcDate)} {...datePickerPropsEndOfDay} />
I also didn't have luck with utcOffset. You could use moment-timezone in your project instead of moment and convert it yourself:
import moment from "moment-timezone";
onDateChange = date => {
console.log("as is:", date.format("YYYY-MM-DD HH:mm:ss"));
console.log("PST:", moment(date).tz("America/Los_Angeles").format("YYYY-MM-DD HH:mm:ss"));
};
Sandbox: https://codesandbox.io/s/y2vwj9mwpz
Since you're using moment.js, you can try using moment.utc() and subtract hours to pst timezone.
moment.utc().startOf('day').subtract(8, 'hours')
I'm strugelling with that one.
I'm trying to build a calendar using react-calendar package. In one place of my app I need to have an access to the calendar month's value. I'm calling the components property onActiveDateChange which I found in docs of the component (react-calendar). It is indeed a callback so I'm trying to use it as my chance to extract month value and send it up to the parent component. But that does not work, not only the value is not changed as expected, but the calendar stops working. Do you know what is causing that? I've tried also with setting a state in the callback but the same results, value is not correct.
Here's my chunk of code:
import React, { Component } from 'react';
import Calendar from 'react-calendar';
import ChooseHour from './ChooseHour';
import { connect } from 'react-redux';
import * as actions from '../actions';
class Calendario extends Component {
state = { showHours: false,}
onChange = date => this.setState({
date }, () => {
const { chosenRoom } = this.props;
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();
const fullDate = `${year}/${month}/${day}`;
const roomAndDayObj = {fullDate, chosenRoom};
this.props.sendRoomAndDay(roomAndDayObj);
}
)
onClickDay(e) {
this.setState({ showHours: true });
}
passActiveDate(activeDate) {
const monthAfterPress = activeDate.getMonth() + 1;
console.log(monthAfterPress);
// this is weird in my opinion, I can log it and it works as expected,
// when I'm firing the next line the values are incorrect
// this.props.activeMonthToPass(monthAfterPress); //this line makes a problem
}
render() {
const { booked } = this.props;
return (
<div>
<div className="calendarsCont">
<Calendar
onChange={this.onChange}
onClickDay={(e) => this.onClickDay(e)}
onActiveDateChange={({ activeStartDate }) => this.passActiveDate(activeStartDate)}
value={this.state.date}
locale="pl-PL"
tileDisabled={({date, view }) =>
date.getDate()===15 && date.getMonth()===6 && date.getFullYear()===2018}
/>
</div>
<div>
{this.state.showHours ?
<ChooseHour chosenDay={this.state.date} chosenRoom={this.props.chosenRoom}/> :
null}
</div>
</div>
)
}
}
export default connect (null, actions)(Calendario);