From 578edb5ca29a2c8aa6cc8ff57fdc9156639a3ea3 Mon Sep 17 00:00:00 2001 From: ddivernois Date: Tue, 31 Jan 2017 17:20:34 +0100 Subject: [PATCH] feat(datepicker): keep day/month when adding/removing month/year in ngb-calendar BREAKING CHANGES: ngb-calendar getNext/getPrev specifications changed `calendar.getNext()`/`calendar.getPrev()` with the period parameter set as `'m'`/`'y'` no longer automatically returns the first day of the month / the first day of the year. The day in the month and the month in the year of the input date are now kept as much as possible. In case the destination month is is not long enough to contain a day with the same number, the last day of the month is used. For example: `calendar.getNext(new NgbDate(2016, 7, 22), 'm'))` previously returned: `new NgbDate(2016, 8, 1))` and now it returns: `new NgbDate(2016, 8, 22))` `calendar.getNext(new NgbDate(2016, 7, 22), 'y'))` previously returned: `new NgbDate(2017, 1, 1))` and now it returns: `new NgbDate(2017, 7, 22))` `calendar.getNext(new NgbDate(2016, 1, 30), 'm'))` previously returned: `new NgbDate(2016, 2, 1))` and now it returns: `new NgbDate(2016, 2, 29))` (because 2016-02-30 is not a valid date) --- src/datepicker/ngb-calendar.spec.ts | 32 +++++++++++++++++++++-------- src/datepicker/ngb-calendar.ts | 19 +++++++++++++++-- 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/src/datepicker/ngb-calendar.spec.ts b/src/datepicker/ngb-calendar.spec.ts index 94b087c7b4..a60bf48bd5 100644 --- a/src/datepicker/ngb-calendar.spec.ts +++ b/src/datepicker/ngb-calendar.spec.ts @@ -44,25 +44,41 @@ describe('ngb-calendar-gregorian', () => { }); it('should add months to date', () => { - expect(calendar.getNext(new NgbDate(2016, 7, 22), 'm')).toEqual(new NgbDate(2016, 8, 1)); + expect(calendar.getNext(new NgbDate(2016, 7, 22), 'm')).toEqual(new NgbDate(2016, 8, 22)); expect(calendar.getNext(new NgbDate(2016, 7, 1), 'm')).toEqual(new NgbDate(2016, 8, 1)); - expect(calendar.getNext(new NgbDate(2016, 12, 22), 'm')).toEqual(new NgbDate(2017, 1, 1)); + expect(calendar.getNext(new NgbDate(2016, 12, 22), 'm')).toEqual(new NgbDate(2017, 1, 22)); + expect(calendar.getNext(new NgbDate(2016, 1, 29), 'm')).toEqual(new NgbDate(2016, 2, 29)); + expect(calendar.getNext(new NgbDate(2016, 1, 30), 'm')).toEqual(new NgbDate(2016, 2, 29)); + expect(calendar.getNext(new NgbDate(2016, 10, 30), 'm', 6)).toEqual(new NgbDate(2017, 4, 30)); + expect(calendar.getNext(new NgbDate(2016, 10, 31), 'm', 6)).toEqual(new NgbDate(2017, 4, 30)); }); it('should subtract months from date', () => { - expect(calendar.getPrev(new NgbDate(2016, 7, 22), 'm')).toEqual(new NgbDate(2016, 6, 1)); + expect(calendar.getPrev(new NgbDate(2016, 7, 22), 'm')).toEqual(new NgbDate(2016, 6, 22)); expect(calendar.getPrev(new NgbDate(2016, 8, 1), 'm')).toEqual(new NgbDate(2016, 7, 1)); - expect(calendar.getPrev(new NgbDate(2017, 1, 22), 'm')).toEqual(new NgbDate(2016, 12, 1)); + expect(calendar.getPrev(new NgbDate(2017, 1, 22), 'm')).toEqual(new NgbDate(2016, 12, 22)); + expect(calendar.getPrev(new NgbDate(2016, 3, 29), 'm')).toEqual(new NgbDate(2016, 2, 29)); + expect(calendar.getPrev(new NgbDate(2016, 3, 30), 'm')).toEqual(new NgbDate(2016, 2, 29)); + expect(calendar.getPrev(new NgbDate(2016, 10, 30), 'm', 4)).toEqual(new NgbDate(2016, 6, 30)); + expect(calendar.getPrev(new NgbDate(2016, 10, 31), 'm', 4)).toEqual(new NgbDate(2016, 6, 30)); }); it('should add years to date', () => { - expect(calendar.getNext(new NgbDate(2016, 1, 22), 'y')).toEqual(new NgbDate(2017, 1, 1)); - expect(calendar.getNext(new NgbDate(2017, 12, 22), 'y')).toEqual(new NgbDate(2018, 1, 1)); + expect(calendar.getNext(new NgbDate(2016, 1, 22), 'y')).toEqual(new NgbDate(2017, 1, 22)); + expect(calendar.getNext(new NgbDate(2017, 12, 22), 'y')).toEqual(new NgbDate(2018, 12, 22)); + expect(calendar.getNext(new NgbDate(2016, 2, 29), 'y')).toEqual(new NgbDate(2017, 2, 28)); + expect(calendar.getNext(new NgbDate(2016, 2, 28), 'y')).toEqual(new NgbDate(2017, 2, 28)); + expect(calendar.getNext(new NgbDate(2016, 2, 29), 'y', 4)).toEqual(new NgbDate(2020, 2, 29)); + expect(calendar.getNext(new NgbDate(2016, 2, 29), 'y', 3)).toEqual(new NgbDate(2019, 2, 28)); }); it('should subtract years from date', () => { - expect(calendar.getPrev(new NgbDate(2016, 12, 22), 'y')).toEqual(new NgbDate(2015, 1, 1)); - expect(calendar.getPrev(new NgbDate(2017, 1, 22), 'y')).toEqual(new NgbDate(2016, 1, 1)); + expect(calendar.getPrev(new NgbDate(2016, 12, 22), 'y')).toEqual(new NgbDate(2015, 12, 22)); + expect(calendar.getPrev(new NgbDate(2017, 1, 22), 'y')).toEqual(new NgbDate(2016, 1, 22)); + expect(calendar.getPrev(new NgbDate(2016, 2, 28), 'y')).toEqual(new NgbDate(2015, 2, 28)); + expect(calendar.getPrev(new NgbDate(2016, 2, 29), 'y')).toEqual(new NgbDate(2015, 2, 28)); + expect(calendar.getPrev(new NgbDate(2016, 2, 29), 'y', 4)).toEqual(new NgbDate(2012, 2, 29)); + expect(calendar.getPrev(new NgbDate(2016, 2, 29), 'y', 3)).toEqual(new NgbDate(2013, 2, 28)); }); it('should check that NgbDate is a valid javascript date', () => { diff --git a/src/datepicker/ngb-calendar.ts b/src/datepicker/ngb-calendar.ts index 3d50aed8dc..4d00d58835 100644 --- a/src/datepicker/ngb-calendar.ts +++ b/src/datepicker/ngb-calendar.ts @@ -43,20 +43,35 @@ export class NgbCalendarGregorian extends NgbCalendar { getNext(date: NgbDate, period: NgbPeriod = 'd', number = 1) { let jsDate = toJSDate(date); + let checkMonth = true; + let expectedMonth = jsDate.getMonth(); switch (period) { case 'y': - return new NgbDate(date.year + number, 1, 1); + jsDate.setFullYear(jsDate.getFullYear() + number); + break; case 'm': - jsDate = new Date(date.year, date.month + number - 1, 1); + expectedMonth += number; + jsDate.setMonth(expectedMonth); + expectedMonth = expectedMonth % 12; + if (expectedMonth < 0) { + expectedMonth = expectedMonth + 12; + } break; case 'd': jsDate.setDate(jsDate.getDate() + number); + checkMonth = false; break; default: return date; } + if (checkMonth && jsDate.getMonth() !== expectedMonth) { + // this means the destination month has less days than the initial month + // let's go back to the end of the previous month: + jsDate.setDate(0); + } + return fromJSDate(jsDate); }