Skip to content

Commit

Permalink
Added more quarter functions (#14)
Browse files Browse the repository at this point in the history
Co-authored-by: Rob Blackbourn <rob.blackbourn@gmail.com>
  • Loading branch information
rob-blackbourn and Rob Blackbourn committed Jun 18, 2023
1 parent 58342c7 commit 6756e92
Show file tree
Hide file tree
Showing 14 changed files with 234 additions and 21 deletions.
21 changes: 21 additions & 0 deletions src/endOfQuarter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { tzLocal } from './LocalTimezone'
import { Timezone } from './Timezone'
import { daysInMonth } from './daysInMonth'
import { quarterOfYear } from './quarterOfYear'

/**
* Find the last moment of the quarter.
*
* @category Anchors
*
* @param date A date
* @param tz An optional timezone. Defaults to the local timezone.
* @returns The last moment of the quarter.
*/
export function endOfQuarter(date: Date, tz: Timezone = tzLocal): Date {
const quarter = quarterOfYear(date, tz)
const monthIndex = 3 * (quarter - 1) + 2
const year = tz.year(date)
const day = daysInMonth(year, monthIndex)
return tz.makeDate(year, monthIndex, day, 23, 59, 59, 999)
}
4 changes: 2 additions & 2 deletions src/findEndOfMonthIndex.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isEndOfMonth } from './isEndOfMonth'
import { isLastDayOfMonth } from './isLastDayOfMonth'
import { tzLocal } from './LocalTimezone'
import { Timezone } from './Timezone'

Expand All @@ -13,5 +13,5 @@ export function findEndOfMonthIndex(
dates: Date[],
tz: Timezone = tzLocal
): number {
return dates.findIndex(date => isEndOfMonth(date, tz))
return dates.findIndex(date => isLastDayOfMonth(date, tz))
}
4 changes: 2 additions & 2 deletions src/findLastEndOfMonthIndex.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isEndOfMonth } from './isEndOfMonth'
import { isLastDayOfMonth } from './isLastDayOfMonth'
import { tzLocal } from './LocalTimezone'
import { Timezone } from './Timezone'
import { findLastIndex } from './utils'
Expand All @@ -14,5 +14,5 @@ export function findLastEndOfMonthIndex(
dates: Date[],
tz: Timezone = tzLocal
): number {
return findLastIndex(dates, date => isEndOfMonth(date, tz))
return findLastIndex(dates, date => isLastDayOfMonth(date, tz))
}
6 changes: 5 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export { endOfDay } from './endOfDay'
export { endOfHour } from './endOfHour'
export { endOfMinute } from './endOfMinute'
export { endOfMonth } from './endOfMonth'
export { endOfQuarter } from './endOfQuarter'
export { endOfSecond } from './endOfSecond'
export { endOfToday } from './endOfToday'
export { endOfWeek } from './endOfWeek'
Expand All @@ -58,12 +59,14 @@ export { isDateEqual } from './isDateEqual'
export { isDateNotEqual } from './isDateNotEqual'
export { isDateOnOrAfter } from './isDateOnOrAfter'
export { isDateOnOrBefore } from './isDateOnOrBefore'
export { isEndOfMonth } from './isEndOfMonth'
export { isLastDayOfMonth } from './isLastDayOfMonth'
export { isLastDayOfQuarter } from './isLastDayOfQuarter'
export { isLeapYear } from './isLeapYear'
export { isoWeekDate } from './isoWeekDate'
export { isoWeekOfYear } from './isoWeekOfYear'
export { isValidDate } from './isValidDate'
export { lastDayOfMonth } from './lastDayOfMonth'
export { lastDayOfQuarter } from './lastDayOfQuarter'
export { lastDayOfWeek } from './lastDayOfWeek'
export { lastDayOfWeekday } from './lastDayOfWeekday'
export { lastDayOfYear } from './lastDayOfYear'
Expand All @@ -89,6 +92,7 @@ export { startOfHour } from './startOfHour'
export { startOfISOWeek } from './startOfISOWeek'
export { startOfMinute } from './startOfMinute'
export { startOfMonth } from './startOfMonth'
export { startOfQuarter } from './startOfQuarter'
export { startOfSecond } from './startOfSecond'
export { startOfWeek } from './startOfWeek'
export { startOfWeekday } from './startOfWeekday'
Expand Down
9 changes: 2 additions & 7 deletions src/isEndOfMonth.ts → src/isLastDayOfMonth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,6 @@ import { Timezone } from './Timezone'
* @param tz An optional timezone. Defaults to the local timezone.
* @returns True if the date is the last day of the month.
*/
export function isEndOfMonth(date: Date, tz: Timezone = tzLocal): boolean {
const { year, monthIndex, day } = tz.dateParts(date, {
year: true,
monthIndex: true,
day: true
})
return day === daysInMonth(year, monthIndex)
export function isLastDayOfMonth(date: Date, tz: Timezone = tzLocal): boolean {
return tz.day(date) === daysInMonth(tz.year(date), tz.monthIndex(date))
}
21 changes: 21 additions & 0 deletions src/isLastDayOfQuarter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { isDateEqual } from './isDateEqual'
import { lastDayOfQuarter } from './lastDayOfQuarter'
import { tzLocal } from './LocalTimezone'
import { Timezone } from './Timezone'

/**
* Check if the date is the last day of the quarter.
*
* @category Calendars
*
* @param date The date to check.
* @param tz An optional timezone. Defaults to the local timezone.
* @returns True if the date is the last day of the quarter.
*/
export function isLastDayOfQuarter(
date: Date,
tz: Timezone = tzLocal
): boolean {
const end = lastDayOfQuarter(date, tz)
return isDateEqual(date, end)
}
21 changes: 21 additions & 0 deletions src/lastDayOfQuarter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { tzLocal } from './LocalTimezone'
import { Timezone } from './Timezone'
import { daysInMonth } from './daysInMonth'
import { quarterOfYear } from './quarterOfYear'

/**
* Find the last day of the quarter.
*
* @category Anchors
*
* @param date The start date.
* @param tz An optional timezone. Defaults to the local timezone.
* @returns A date which is the last day of the quarter.
*/
export function lastDayOfQuarter(date: Date, tz: Timezone = tzLocal): Date {
const quarter = quarterOfYear(date, tz)
const monthIndex = 3 * (quarter - 1) + 2
const year = tz.year(date)
const day = daysInMonth(year, monthIndex)
return tz.makeDate(year, monthIndex, day)
}
2 changes: 1 addition & 1 deletion src/startOfMonth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { tzLocal } from './LocalTimezone'
import { Timezone } from './Timezone'

/**
* Find the start of the months for a given date.
* Find the start of the month for a given date.
*
* @category Anchors
*
Expand Down
18 changes: 18 additions & 0 deletions src/startOfQuarter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { tzLocal } from './LocalTimezone'
import { Timezone } from './Timezone'
import { quarterOfYear } from './quarterOfYear'

/**
* Find the start of the quarter for a given date.
*
* @category Anchors
*
* @param date The date.
* @param tz An optional timezone. Defaults to the local timezone.
* @returns The start of the quarter.
*/
export function startOfQuarter(date: Date, tz: Timezone = tzLocal): Date {
const quarter = quarterOfYear(date, tz)
const monthIndex = 3 * (quarter - 1)
return tz.makeDate(tz.year(date), monthIndex, 1)
}
31 changes: 31 additions & 0 deletions test/endOfQuarter.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {
IANATimezone,
dataToTimezoneOffset,
endOfQuarter,
tzLocal,
tzUtc
} from '../src'
import chicagoTzData from '@jetblack/tzdata/dist/latest/America/Chicago.json'
import tokyoTzData from '@jetblack/tzdata/dist/latest/Asia/Tokyo.json'

describe('endOfQuarter', () => {
const tzChicago = new IANATimezone(
'America/Chicago',
chicagoTzData.map(dataToTimezoneOffset)
)
const tzTokyo = new IANATimezone(
'Asia/Tokyo',
tokyoTzData.map(dataToTimezoneOffset)
)

for (const tz of [tzUtc, tzLocal, tzChicago, tzTokyo]) {
describe(tz.name, () => {
it('should find the last moment of the quarter', () => {
const date = tz.makeDate(2000, 0, 1)
const actual = endOfQuarter(date, tz)
const expected = tz.makeDate(2000, 2, 31, 23, 59, 59, 999)
expect(tz.toISOString(actual)).toBe(tz.toISOString(expected))
})
})
}
})
16 changes: 8 additions & 8 deletions test/isEndOfMonth.test.ts → test/isLastDayOfMonth.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import {
IANATimezone,
dataToTimezoneOffset,
isEndOfMonth,
isLastDayOfMonth,
tzLocal,
tzUtc
} from '../src'
import chicagoTzData from '@jetblack/tzdata/dist/latest/America/Chicago.json'
import tokyoTzData from '@jetblack/tzdata/dist/latest/Asia/Tokyo.json'

describe('isEndOfMonth', () => {
describe('isLastDayOfMonth', () => {
const tzChicago = new IANATimezone(
'America/Chicago',
chicagoTzData.map(dataToTimezoneOffset)
Expand All @@ -22,27 +22,27 @@ describe('isEndOfMonth', () => {
describe(tz.name, () => {
it('should know 30 January is not the end of the month', () => {
const date = tz.makeDate(2008, 0, 30)
expect(isEndOfMonth(date, tz)).toBeFalsy()
expect(isLastDayOfMonth(date, tz)).toBeFalsy()
})

it('should know 31 January is not the end of the month', () => {
it('should know 31 January is the end of the month', () => {
const date = tz.makeDate(2008, 0, 31)
expect(isEndOfMonth(date, tz)).toBeTruthy()
expect(isLastDayOfMonth(date, tz)).toBeTruthy()
})

it('should know 28 February 2008 is not the end of the month because 2008 is a leap year', () => {
const date = tz.makeDate(2008, 1, 28)
expect(isEndOfMonth(date, tz)).toBeFalsy()
expect(isLastDayOfMonth(date, tz)).toBeFalsy()
})

it('should know 29 February 2008 is the end of the month because 2008 is a leap year', () => {
const date = tz.makeDate(2008, 1, 29)
expect(isEndOfMonth(date, tz)).toBeTruthy()
expect(isLastDayOfMonth(date, tz)).toBeTruthy()
})

it('should know 28 February 2009 is the end of the month because 2008 is not a leap year', () => {
const date = tz.makeDate(2009, 1, 28)
expect(isEndOfMonth(date, tz)).toBeTruthy()
expect(isLastDayOfMonth(date, tz)).toBeTruthy()
})
})
}
Expand Down
34 changes: 34 additions & 0 deletions test/isLastDayOfQuarter.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {
IANATimezone,
dataToTimezoneOffset,
isLastDayOfQuarter,
tzLocal,
tzUtc
} from '../src'
import chicagoTzData from '@jetblack/tzdata/dist/latest/America/Chicago.json'
import tokyoTzData from '@jetblack/tzdata/dist/latest/Asia/Tokyo.json'

describe('isLastDayOfQuarter', () => {
const tzChicago = new IANATimezone(
'America/Chicago',
chicagoTzData.map(dataToTimezoneOffset)
)
const tzTokyo = new IANATimezone(
'Asia/Tokyo',
tokyoTzData.map(dataToTimezoneOffset)
)

for (const tz of [tzUtc, tzLocal, tzChicago, tzTokyo]) {
describe(tz.name, () => {
it('should know 30 January is not the end of the quarter', () => {
const date = tz.makeDate(2008, 0, 30)
expect(isLastDayOfQuarter(date, tz)).toBeFalsy()
})

it('should know 31 March is the end of the month', () => {
const date = tz.makeDate(2008, 2, 31)
expect(isLastDayOfQuarter(date, tz)).toBeTruthy()
})
})
}
})
30 changes: 30 additions & 0 deletions test/lastDayOfQuarter.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {
IANATimezone,
dataToTimezoneOffset,
lastDayOfQuarter,
tzLocal,
tzUtc
} from '../src'
import chicagoTzData from '@jetblack/tzdata/dist/latest/America/Chicago.json'
import tokyoTzData from '@jetblack/tzdata/dist/latest/Asia/Tokyo.json'

describe('lastDayOfQuarter', () => {
const tzChicago = new IANATimezone(
'America/Chicago',
chicagoTzData.map(dataToTimezoneOffset)
)
const tzTokyo = new IANATimezone(
'Asia/Tokyo',
tokyoTzData.map(dataToTimezoneOffset)
)

for (const tz of [tzUtc, tzLocal, tzChicago, tzTokyo]) {
describe(tz.name, () => {
it('should find the last day of the quarter', () => {
const actual = lastDayOfQuarter(tz.makeDate(2000, 0, 1), tz)
const expected = tz.makeDate(2000, 2, 31)
expect(tz.toISOString(actual)).toBe(tz.toISOString(expected))
})
})
}
})
38 changes: 38 additions & 0 deletions test/startOfQuarter.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {
IANATimezone,
dataToTimezoneOffset,
startOfQuarter,
tzLocal,
tzUtc
} from '../src'
import chicagoTzData from '@jetblack/tzdata/dist/latest/America/Chicago.json'
import tokyoTzData from '@jetblack/tzdata/dist/latest/Asia/Tokyo.json'

describe('startOfQuarter', () => {
const tzChicago = new IANATimezone(
'America/Chicago',
chicagoTzData.map(dataToTimezoneOffset)
)
const tzTokyo = new IANATimezone(
'Asia/Tokyo',
tokyoTzData.map(dataToTimezoneOffset)
)

for (const tz of [tzUtc, tzLocal, tzChicago, tzTokyo]) {
describe(tz.name, () => {
it('should return the start of the quarters', () => {
const date = tz.makeDate(2014, 8, 2, 2, 11, 55, 664)
const actual = startOfQuarter(date, tz)
const expected = tz.makeDate(2014, 6, 1)
expect(tz.toISOString(actual)).toBe(tz.toISOString(expected))
})

it('should not change date', () => {
const date = tz.makeDate(2014, 6, 1)
const actual = startOfQuarter(date, tz)
const expected = tz.makeDate(2014, 6, 1)
expect(tz.toISOString(actual)).toBe(tz.toISOString(expected))
})
})
}
})

0 comments on commit 6756e92

Please sign in to comment.