Mimz
Mimz

Reputation: 13

CYPRESS: How to add one month to my current date with consideration to months like February(28 days) and months that have 30 days?

I have this cypress test where Im checking for a correct billing date. Our website has monthly subscriptions and it works as follows: If you start your subscription on January 31st, your next billing date will automatically be on the 1st of March since February has only 28 days. Same if you start your subscription on the 31st of March, then your next billing date will be on the first of 1st of May since there is no 31st in April and it automatically changes to the first day of the next month. Starting on other normal dates like the 15th will always be the same date (15th) of the next month etc..

My issue is when testing this with cypress, i always get the last day of the next month. For example if i test that Im gonna start my subscription on the 31st of March, my test will have 30th of April as an expected result, which is not correct since i want the expected result of my test to be 1st of May.

I am using this function but i cant seem to make it work properly since there are many differences in the months.

export const getBillingDate = (todayDate: string, months: number) => {
  const date1 = new Date(todayDate)
  const date2 = new Date(todayDate)
  date1.setDate(1)
  const daysInNextMonth = getDaysInMonth(addMonths(date1, months))
  date2.setDate(Math.min(date2.getDate(), daysInNextMonth))
  return format(addMonths(date2, months), 'MMMM do, yyyy')
}

I would really appreciate anyone's help with this since i am new to Cypress and testing in general. (Sorry english is not my first language)

Upvotes: 1

Views: 2859

Answers (5)

user16815174
user16815174

Reputation: 1

I use 2 ways to add half a year to today's date

1.

var now = new Date();
now.setMonth(now.getMonth() + 6);
var dd = String(now.getDate()).padStart(2, '0');
var mm = String(now.getMonth() + 1).padStart(2, '0'); //January is 0!
var yyyy = now.getFullYear();
var next_bill_date = dd + '/' + mm + '/' + yyyy + "

Result e.g.

  • Today date = "28/08/2024" next_bill_date = 28/02/2025"
  • Today date = "29/08/2024" next_bill_date = 01/03/2025"
  • Today date = "30/08/2024" next_bill_date = 02/03/2025"
  • Today date = "31/08/2024" next_bill_date = 01/03/2025"
import dayjs from 'dayjs'

const next_bill_date = dayjs()
.add(6, 'month')
.format('DD/MM/YYYY')

return next_bill_date;

Result e.g.

  • Today date = "28/08/2024" next_bill_date = 28/02/2025"
  • Today date = "29/08/2024" next_bill_date = 28/02/2025"
  • Today date = "30/08/2024" next_bill_date = 28/02/2025"
  • Today date = "31/08/2024" next_bill_date = 28/02/2025"

Upvotes: 0

TesterDick
TesterDick

Reputation: 10545

You have a months parameter, if you want to increase by more that one month you should calculate each month separately.

'dayjs` definitely gives you more options to play with.

const expect = chai.expect

const addBillingMonth = (start) => {
  let next = start.add(1, 'month')
  if (start.date() !== next.date()) {
    next = next.add(1, 'month').startOf('month')
  }
  return next  
}

const getBilling = (startDate, months = 1) => {
  let result = dayjs(startDate)
  for (let i = 0; i < months; i++) {
    result = addBillingMonth(result)  // repeat for each month
  }
  return result.format('YYYY/MM/DD')
}

expect(getBilling('2022/01/15')).to.eq('2022/02/15')
expect(getBilling('2022/01/31')).to.eq('2022/03/01')
expect(getBilling('2022/02/15')).to.eq('2022/03/15')
expect(getBilling('2022/02/28')).to.eq('2022/03/28')
expect(getBilling('2022/03/15')).to.eq('2022/04/15')
expect(getBilling('2022/03/31')).to.eq('2022/05/01')
<script src="https://cdnjs.cloudflare.com/ajax/libs/chai/4.3.6/chai.min.js"></script>
<script src="https://unpkg.com/[email protected]/dayjs.min.js"></script>

Upvotes: 2

RobG
RobG

Reputation: 147363

Your requirements are a little unusual. Typically when adding a month and it overflows, the requirement is to return the last day of the month, not the first of the following month. But it's not difficult, just get the starting date (day in month), add a month, and if the resulting date isn't the same, set it to the 1st, e.g.:

function addBillingMonth(date = new Date()) {
  let d = new Date(+date);
  let dayNum = d.getDate();
  d.setMonth(d.getMonth() + 1);
  if (dayNum !== d.getDate()) {
    d.setDate(1);
  }
  return d;
}

// Examples
[ new Date(2021,11,31), // 31 Dec
  new Date(2022, 0,15), // 15 Jan
  new Date(2022, 0,31), // 31 Jan
  new Date(2022, 2,31), // 31 Mar
].forEach(d => console.log(d.toDateString() +
  ' next bill: ' + addBillingMonth(d).toDateString())
);
  

Upvotes: 0

Fody
Fody

Reputation: 31904

Both dayjs and javascript new Date() fail to add all the dates exactly as you want.

But you can use dayjs().daysInMonth() to get results exactly as per your description,

const getBilling = (startDate) => {
  const [year, month, day] = startDate.split('/')

  const sd = dayjs(startDate)
  const firstOfNextMonth = sd.month(sd.month() + 1).date(1)
  const daysInNextMonth = dayjs(firstOfNextMonth).daysInMonth()

  let end;
  if (daysInNextMonth < day) {
    end = `${year}/${+month+2}/${1}`  // always bump to 1st day of month + 2
  } else {
    end = `${year}/${+month+1}/${day}`
  }

  return dayjs(end, 'YYYY/MM/DD').format('YYYY/MM/DD')
}

it('gets billing date, accounting for short months', () => {

  //Jan
  expect(getBilling('2022/01/15')).to.eq('2022/02/15')
  expect(getBilling('2022/01/31')).to.eq('2022/03/01')

  //Feb
  expect(getBilling('2022/02/15')).to.eq('2022/03/15')
  expect(getBilling('2022/02/28')).to.eq('2022/03/28')

  //Mar
  expect(getBilling('2022/03/15')).to.eq('2022/04/15')
  expect(getBilling('2022/03/31')).to.eq('2022/05/01')

})

enter image description here

Upvotes: 2

jjhelguero
jjhelguero

Reputation: 2571

Day.js already exists to do date math.

You can use their .add() to add 30 days to a date dayjs().add(30, 'day').

You can also format the dates with .format() to format the way you want it dayjs('2019-01-25').format('DD/MM/YYYY') // '25/01/2019'

Upvotes: 0

Related Questions