134

I've run into a problem where I have to store the initial values of a moment object but I'm having some trouble preventing my variable from changing along with the original object.

Unfortunately Object.freeze() doesn't work, because moment.js returns an Invalid date error when I try to format that.

zhulien
  • 5,145
  • 3
  • 22
  • 36
Shengbo1618
  • 1,443
  • 2
  • 9
  • 5
  • 4
    And the code looks like…? If you want to store the initial value, store the time value, available using the *valueOf* method or implicit conversion to number. – RobG Jun 22 '15 at 11:48
  • once your variable is set, it is set, it wont change automagically, so rather look to not set it again and again – john Smith Jun 22 '15 at 11:48

3 Answers3

216

There's a Moment.js plugin on NPM called frozen-moment - You could use moment().freeze() in place of Object.freeze(moment()).

Otherwise, vanilla Moment.js has a clone method that should help you avoid mutability problems, so you could do something like this:

var a = moment(),
    b = a.clone(); // or moment(a)

UPDATE:

It has been two years since I wrote this answer. In this time, another library for working with dates has surfaced and gained a lot of traction: https://date-fns.org/

This library is immutable by default and follows a modular, functional architecture, which means it is better suited to tree shaking and client-side bundling. If you are working on a project that makes extensive use of Webpack on the client side, and find that Moment.js is giving you trouble with your build, or even if Moment.js' mutability is causing you a lot of headache, then you should give date-fns a try.

razorbeard
  • 2,924
  • 1
  • 22
  • 29
  • Well, I'm using moment.js in the fullCalendar plugin and it turns out I was getting the moment object data from a later state of my event than I was supposed to. Mutability issues are definitely a thing with moment.js though, so thank you very much for the suggestion and sorry I wasted your time. – Shengbo1618 Jun 22 '15 at 12:28
  • 27
    You can manipulate stored `moment` variable without mutating it: just use clone() like this: `zz = moment(); zz.clone().add(3, 'h').toISOString();` – Quake1TF Feb 18 '16 at 13:29
  • 5
    Note that date-fns has very poor timezone support and no support for UTC dates. – mjuopperi Jun 10 '18 at 10:19
  • 4
    I've been using `date-fns` for a while now but have since had to jump into legacy code using Moment and boy, did this post save me from jumping out of a window. – Yuschick Feb 21 '19 at 09:13
  • [`dayjs`](https://github.com/iamkun/dayjs) is also a good alternative since it has APIs similar to Moment.js with immutable nature. (As of March 2019, it lacks timezone support but this is a fairly new library and I can observe that the work is ongoing.) – Tomoyuki Aota Apr 04 '19 at 17:49
4

It's an old question and apologies for shameless self promotion as this is not my intention, just hope it will help someone.

In addition to what razorbeard says (.clone() etc) I created NPM module that attaches immutable methods to whatever Moment.js comes with out of the box. The intention is not to break existing code so module adds new methods with Immu appended to its name.

Each instance returned by moment factory will be decorated with immutable methods e.g moment().startOf() will have corresponding startOfImmu(), add() will have addImmu() etc. Each of those returns new moment rather then modifying existing one. To use it just pass moment factory to momentImmutableMethods to get access to new immutable methods. Example:

var moment = require('moment'); // or moment-timezone 
import { momentImmutableMethods } from 'moment-immutable-methods';

// to decorate instances with immutable methods we need to extend moment factory as below:
momentImmutableMethods(moment);

// now every instance returned by moment will have Immu methods attached.


// IMMUTABLE EXAMPLE
// we using immutable methods that were attached to every instance, these have Immu appended to original name
const ddd = moment({
  hour: 5,
  minute: 10
});
// Moment {_isAMomentObject: true, _i: {…}, _isUTC: false, _pf: {…}, _locale: Locale, …}
const eee = ddd.startOfImmu('day');
// Moment {_isAMomentObject: true, _i: {…}, _isUTC: false, _pf: {…}, _locale: Locale, …}
console.log(ddd === eee);
// false
const fff = eee.startOfImmu('month');
// Moment {_isAMomentObject: true, _i: {…}, _isUTC: false, _pf: {…}, _locale: Locale, …}
console.log(ddd === fff);
// false
console.log(eee === fff);
// false
console.log(ddd.format('DD/MM/YY HH:mma'));
// "14/04/18 05:10am"
console.log(eee.format('DD/MM/YY HH:mma'));
// "14/04/18 00:00am"
console.log(fff.format('DD/MM/YY HH:mma'));
// "08/04/18 00:00am"

Its on NPM at https://www.npmjs.com/package/moment-immutable-methods

spirytus
  • 10,726
  • 14
  • 61
  • 75
0

You can also use day.js library which has almost the exact API of moment.js but is immutable.

Sinandro
  • 2,426
  • 3
  • 21
  • 36