react-big-calendar: date[("get" + method)] is not a function

I’m a Rails dev and new to Node/Webpack/React. When I switch between month/week/day view, the app often crashes and I get this error in the console: index.js?07ad:209 Uncaught TypeError: date[("get" + method)] is not a function.

<BigCalendar
  defaultDate={new Date()}
  defaultView='week'
  events={this.state.events}
/>

I wonder whether this has to do with how webpack is configured and compiles the files? Here’s the file that causes the error, the problem is on line 209:

var MILI    = 'milliseconds'
  , SECONDS = 'seconds'
  , MINUTES = 'minutes'
  , HOURS   = 'hours'
  , DAY     = 'day'
  , WEEK    = 'week'
  , MONTH   = 'month'
  , YEAR    = 'year'
  , DECADE  = 'decade'
  , CENTURY = 'century';

var dates = module.exports = {

  add: function(date, num, unit) {
    date = new Date(date)

    switch (unit){
      case MILI:
      case SECONDS:
      case MINUTES:
      case HOURS:
      case YEAR:
        return dates[unit](date, dates[unit](date) + num)
      case DAY:
        return dates.date(date, dates.date(date) + num)
      case WEEK:
        return dates.date(date, dates.date(date) + (7 * num)) 
      case MONTH:
        return monthMath(date, num)
      case DECADE:
        return dates.year(date, dates.year(date) + (num * 10))
      case CENTURY:
        return dates.year(date, dates.year(date) + (num * 100))
    }

    throw new TypeError('Invalid units: "' + unit + '"')
  },

  subtract: function(date, num, unit) {
    return dates.add(date, -num, unit)
  },

  startOf: function(date, unit, firstOfWeek) {
    date = new Date(date)

    switch (unit) {
      case 'century':
      case 'decade':
      case 'year':
          date = dates.month(date, 0);
      case 'month':
          date = dates.date(date, 1);
      case 'week':
      case 'day':
          date = dates.hours(date, 0);
      case 'hours':
          date = dates.minutes(date, 0);
      case 'minutes':
          date = dates.seconds(date, 0);
      case 'seconds':
          date = dates.milliseconds(date, 0);
    }

    if (unit === DECADE) 
      date = dates.subtract(date, dates.year(date) % 10, 'year')

    if (unit === CENTURY) 
      date = dates.subtract(date, dates.year(date) % 100, 'year')

    if (unit === WEEK) 
      date = dates.weekday(date, 0, firstOfWeek);

    return date
  },


  endOf: function(date, unit, firstOfWeek){
    date = new Date(date)
    date = dates.startOf(date, unit, firstOfWeek)
    date = dates.add(date, 1, unit)
    date = dates.subtract(date, 1, MILI)
    return date
  },

  eq:  createComparer(function(a, b){ return a === b }),
  neq: createComparer(function(a, b){ return a !== b }),
  gt:  createComparer(function(a, b){ return a > b }),
  gte: createComparer(function(a, b){ return a >= b }),
  lt:  createComparer(function(a, b){ return a < b }),
  lte: createComparer(function(a, b){ return a <= b }),

  min: function(){
    return new Date(Math.min.apply(Math, arguments))
  },

  max: function(){
    return new Date(Math.max.apply(Math, arguments))
  },

  inRange: function(day, min, max, unit){
    unit = unit || 'day'

    return (!min || dates.gte(day, min, unit))
        && (!max || dates.lte(day, max, unit))
  },

  milliseconds:   createAccessor('Milliseconds'),
  seconds:        createAccessor('Seconds'),
  minutes:        createAccessor('Minutes'),
  hours:          createAccessor('Hours'),
  day:            createAccessor('Day'),
  date:           createAccessor('Date'),
  month:          createAccessor('Month'),
  year:           createAccessor('FullYear'),

  decade: function (date, val) {
    return val === undefined 
      ? dates.year(dates.startOf(date, DECADE))
      : dates.add(date, val + 10, YEAR);
  },

  century: function (date, val) {
    return val === undefined 
      ? dates.year(dates.startOf(date, CENTURY))
      : dates.add(date, val + 100, YEAR);
  },

  weekday: function (date, val, firstDay) {
      var weekday = (dates.day(date) + 7 - (firstDay || 0) ) % 7;

      return val === undefined 
        ? weekday 
        : dates.add(date, val - weekday, DAY);
  },

  diff: function (date1, date2, unit, asFloat) {
    var dividend, divisor, result;

    switch (unit) {
      case MILI:
      case SECONDS:
      case MINUTES:
      case HOURS:
      case DAY:
      case WEEK:
        dividend = date2.getTime() - date1.getTime(); break;
      case MONTH:
      case YEAR:
      case DECADE:
      case CENTURY:
        dividend = (dates.year(date2) - dates.year(date1)) * 12 + dates.month(date2) - dates.month(date1); break;
      default:
        throw new TypeError('Invalid units: "' + unit + '"');
    }

    switch (unit) {
      case MILI:
          divisor = 1; break;
      case SECONDS:
          divisor = 1000; break;
      case MINUTES:
          divisor = 1000 * 60; break;
      case HOURS:
          divisor = 1000 * 60 * 60; break;
      case DAY:
          divisor = 1000 * 60 * 60 * 24; break;
      case WEEK:
          divisor = 1000 * 60 * 60 * 24 * 7; break;
      case MONTH:
          divisor = 1; break;
      case YEAR:
          divisor = 12; break;
      case DECADE:
          divisor = 120; break;
      case CENTURY:
          divisor = 1200; break;
      default:
        throw new TypeError('Invalid units: "' + unit + '"');
    }

    result = dividend / divisor;

    return asFloat ? result : absoluteFloor(result);
  }
};

function absoluteFloor(number) {
  return number < 0 ? Math.ceil(number) : Math.floor(number);
}

function monthMath(date, val){
  var current = dates.month(date)
    , newMonth  = (current + val);

    date = dates.month(date, newMonth)

    while (newMonth < 0 ) newMonth = 12 + newMonth

    //month rollover
    if ( dates.month(date) !== ( newMonth % 12))
      date = dates.date(date, 0) //move to last of month

    return date
}

function createAccessor(method){
  return function(date, val){
    if (val === undefined)
      return date['get' + method]()

    date = new Date(date)
    date['set' + method](val)
    return date
  }
}

function createComparer(operator) {
  return function (a, b, unit) {
    return operator(+dates.startOf(a, unit), +dates.startOf(b, unit))
  };
}



/*****************
 ** WEBPACK FOOTER
 ** ./~/date-arithmetic/index.js
 ** module id = 415
 ** module chunks = 0
 **/

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 11
  • Comments: 28 (4 by maintainers)

Most upvoted comments

Oh my bad, the calendar expects only Date objects so moment().toDate()

events={[{
  'title': 'My event',
  'allDay': true,
  'start': moment().toDate(),
  'end': moment().add(4, "hours").toDate()
}]}

More then likely something isn’t a Date object that should be. It may be your event start and end dates

I had the exact same problem. Although the docs for startAccessor clearly say The start date/time of the event. Must resolve to a JavaScript Date object., I totally missed it. Had to set a breakpoint and debug. Maybe we could do a simple check and throw an error if start/end is not a Date. Would probably prevent many issues like this.

So yeah, for anyone having such issues in the future, here’s the gist of it:

  1. when using moment.js as your event dates, you need to go through them and .toDate() them all before passing them to the calendar
  2. if you have strings like @silentlight then go through them all and either new Date(<your date string>) like @jdeniau said or moment(<your date string>, [format]).toDate() if it’s in a format new Date() cannot understand

Basically the calendar only accepts javascript Date objects, not strings or moment objects, so make sure that’s what you give it. This is intended behavior and it’s fine because it makes the calendar library-agnostic, thus avoiding unnecessary dependencies.

I agree with @HriBB on that a type check and a specific error message would improve overall usability of this library.

thanks @DonovanCharpin, this saved me some minutes

Hi, I do not remember the code well too, but it seems like you have a string, and the library requires a Date object.

You can just do something like this : new Date('Tue, 10 Jan 2017 17:33:00 UTC +00:00') and it should be fine

I’m wondering if this parsing the time could be part of the default handling of the lib.

I mean a localizer is already needed to be specified when using this lib, so it’s technically feasible to parse the event object start and end value based on the localizer.

For example, if moment is chosen as the localizer, passing start value as "2019-11-11T00:00:00Z" should automatically parsed by using moment("2019-11-11T00:00:00Z").toDate().

Oh my bad, the calendar expects only Date objects so moment().toDate()

events={[{
  'title': 'My event',
  'allDay': true,
  'start': moment().toDate(),
  'end': moment().add(4, "hours").toDate()
}]}

Hi , I am getting same error , could you please guide me where I can change toDate in code

After I read this issue, I finally got the answer. I’ll leave my solution here, in case there are some guys who faced the same issue when working with this lib and need to store the data to local storage in a React app.

I faced a problem when storing the ‘raw’ start and end Date data to local storage. So I fixed it by reformating the date data to a timestamp and store it in local storage. Then when I need to read it back, I’m reformating it back to date data.

In a componentDidUpdate() function, when I stored the data to local storage:

const newEvents = this.state.events.map((event) => {
  return {
    id: event.id,
    start: new Date(event.start).getTime(),
    end: new Date(event.end).getTime(),
    title: event.title,
  };
});
const events = JSON.stringify(newEvents);
localStorage.setItem("events", events);

and in a componentDidMount I reformating it to a js Date:

const events = localStorage.getItem("events");
const result = JSON.parse(events);
const newEvents = result.map((event) => {
  return {
    id: event.id,
    start: new Date(event.start),
    end: new Date(event.end),
    title: event.title,
  };
});

I’m not too sure whether is it a good solution or not, at least it works just fine for now. It’s midnight here, and I need to sleep🤞

So yeah, for anyone having such issues in the future, here’s the gist of it:

1. when using `moment.js` as your event dates, you need to go through them and `.toDate()` them all before passing them to the calendar

2. if you have strings like @silentlight then go through them all and either `new Date(<your date string>)` like @jdeniau said or `moment(<your date string>, [format]).toDate()` if it's in a format `new Date()` cannot understand

Basically the calendar only accepts javascript Date objects, not strings or moment objects, so make sure that’s what you give it. This is intended behavior and it’s fine because it makes the calendar library-agnostic, thus avoiding unnecessary dependencies.

I agree with @HriBB on that a type check and a specific error message would improve overall usability of this library.

@steodor please help me

Along this line of thinking, what are people’s thoughts on allowing the calendar to accept moment.js objects? Moment.js is already recommended as a localization option, so it’s likely that most consumers of the component are making some use of moment already.