@heinlein-video/rrule
Version:
rrule fork. Includes the src/ folder for typescript sourceMaps
204 lines (177 loc) • 5.07 kB
text/typescript
import { padStart } from './helpers'
import { Time } from './datetime'
type Datelike = Pick<Date, 'getTime'>
export const datetime = function (
y: number,
m: number,
d: number,
h = 0,
i = 0,
s = 0
) {
return new Date(Date.UTC(y, m - 1, d, h, i, s))
}
/**
* General date-related utilities.
* Also handles several incompatibilities between JavaScript and Python
*
*/
export const MONTH_DAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
/**
* Number of milliseconds of one day
*/
export const ONE_DAY = 1000 * 60 * 60 * 24
/**
* @see: <http://docs.python.org/library/datetime.html#datetime.MAXYEAR>
*/
export const MAXYEAR = 9999
/**
* Python uses 1-Jan-1 as the base for calculating ordinals but we don't
* want to confuse the JS engine with milliseconds > Number.MAX_NUMBER,
* therefore we use 1-Jan-1970 instead
*/
export const ORDINAL_BASE = datetime(1970, 1, 1)
/**
* Python: MO-SU: 0 - 6
* JS: SU-SAT 0 - 6
*/
export const PY_WEEKDAYS = [6, 0, 1, 2, 3, 4, 5]
/**
* py_date.timetuple()[7]
*/
export const getYearDay = function (date: Date) {
const dateNoTime = new Date(
date.getUTCFullYear(),
date.getUTCMonth(),
date.getUTCDate()
)
return (
Math.ceil(
(dateNoTime.valueOf() - new Date(date.getUTCFullYear(), 0, 1).valueOf()) /
ONE_DAY
) + 1
)
}
export const isLeapYear = function (year: number) {
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0
}
export const isDate = function (value: unknown): value is Date {
return value instanceof Date
}
export const isValidDate = function (value: unknown): value is Date {
return isDate(value) && !isNaN(value.getTime())
}
/**
* @return {Number} the date's timezone offset in ms
*/
export const tzOffset = function (date: Date) {
return date.getTimezoneOffset() * 60 * 1000
}
/**
* @see: <http://www.mcfedries.com/JavaScript/DaysBetween.asp>
*/
export const daysBetween = function (date1: Date, date2: Date) {
// The number of milliseconds in one day
// Convert both dates to milliseconds
const date1ms = date1.getTime() - tzOffset(date1)
const date2ms = date2.getTime() - tzOffset(date2)
// Calculate the difference in milliseconds
const differencems = date1ms - date2ms
// Convert back to days and return
return Math.round(differencems / ONE_DAY)
}
/**
* @see: <http://docs.python.org/library/datetime.html#datetime.date.toordinal>
*/
export const toOrdinal = function (date: Date) {
return daysBetween(date, ORDINAL_BASE)
}
/**
* @see - <http://docs.python.org/library/datetime.html#datetime.date.fromordinal>
*/
export const fromOrdinal = function (ordinal: number) {
return new Date(ORDINAL_BASE.getTime() + ordinal * ONE_DAY)
}
export const getMonthDays = function (date: Date) {
const month = date.getUTCMonth()
return month === 1 && isLeapYear(date.getUTCFullYear())
? 29
: MONTH_DAYS[month]
}
/**
* @return {Number} python-like weekday
*/
export const getWeekday = function (date: Date) {
return PY_WEEKDAYS[date.getUTCDay()]
}
/**
* @see: <http://docs.python.org/library/calendar.html#calendar.monthrange>
*/
export const monthRange = function (year: number, month: number) {
const date = datetime(year, month + 1, 1)
return [getWeekday(date), getMonthDays(date)]
}
/**
* @see: <http://docs.python.org/library/datetime.html#datetime.datetime.combine>
*/
export const combine = function (date: Date, time: Date | Time) {
time = time || date
return new Date(
Date.UTC(
date.getUTCFullYear(),
date.getUTCMonth(),
date.getUTCDate(),
time.getHours(),
time.getMinutes(),
time.getSeconds(),
time.getMilliseconds()
)
)
}
export const clone = function (date: Date | Time) {
const dolly = new Date(date.getTime())
return dolly
}
export const cloneDates = function (dates: Date[] | Time[]) {
const clones = []
for (let i = 0; i < dates.length; i++) {
clones.push(clone(dates[i]))
}
return clones
}
/**
* Sorts an array of Date or Time objects
*/
export const sort = function <T extends Datelike>(dates: T[]) {
dates.sort(function (a, b) {
return a.getTime() - b.getTime()
})
}
export const timeToUntilString = function (time: number, utc = true) {
const date = new Date(time)
return [
padStart(date.getUTCFullYear().toString(), 4, '0'),
padStart(date.getUTCMonth() + 1, 2, '0'),
padStart(date.getUTCDate(), 2, '0'),
'T',
padStart(date.getUTCHours(), 2, '0'),
padStart(date.getUTCMinutes(), 2, '0'),
padStart(date.getUTCSeconds(), 2, '0'),
utc ? 'Z' : '',
].join('')
}
export const untilStringToDate = function (until: string) {
const re = /^(\d{4})(\d{2})(\d{2})(T(\d{2})(\d{2})(\d{2})Z?)?$/
const bits = re.exec(until)
if (!bits) throw new Error(`Invalid UNTIL value: ${until}`)
return new Date(
Date.UTC(
parseInt(bits[1], 10),
parseInt(bits[2], 10) - 1,
parseInt(bits[3], 10),
parseInt(bits[5], 10) || 0,
parseInt(bits[6], 10) || 0,
parseInt(bits[7], 10) || 0
)
)
}