UNPKG

@botonic/plugin-contentful

Version:

Botonic Plugin Contentful is one of the **[available](https://github.com/hubtype/botonic/tree/master/packages)** plugins for Botonic. **[Contentful](http://www.contentful.com)** is a CMS (Content Management System) which manages contents of a great variet

189 lines (165 loc) 4.76 kB
import momentTz from 'moment-timezone' import { offsetWithTimeZone } from './timezones' /** * Manages ranges of hour/minutes for each day of a week. * The hour/minutes refer to the specified timezone. * Use {@link addException} to add holidays (eg with empty {@link TimeRange}) or sales openings in weekends * TODO consider using everywhere Date.toLocaleTimeString() to remove moment-timezone dependency */ export class Schedule { static TZ_CET = 'Europe/Madrid' private readonly zone: momentTz.MomentZone private readonly scheduleByDay = new Map<WeekDay, DaySchedule>() private readonly exceptions = [] as ExceptionSchedule[] constructor(tzName: string) { const zone = momentTz.tz.zone(tzName) if (!zone) { throw new Error(`${tzName} is not a valid timezone name`) } this.zone = zone } createHourAndMinute(hour: number, minute = 0): HourAndMinute { return new HourAndMinute(hour, minute) } addDaySchedule(weekday: WeekDay, daySchedule: DaySchedule): Schedule { this.scheduleByDay.set(weekday, daySchedule) return this } /** * For the specified date, the weekly schedule will be superseded by the daySchedule specified here */ addException(date: Date, daySchedule: DaySchedule): Schedule { this.exceptions.push(new ExceptionSchedule(date, daySchedule)) return this } /** * Formats the specified date using the {@link Schedule}'s timezone. * @param locales don't confuse with timezone. This is just to format the date */ timeInThisTimezone( locales?: string | string[], date: Date = new Date() ): string { const options: Intl.DateTimeFormatOptions = { timeZone: this.zone.name, hour12: false, } return date.toLocaleTimeString(locales, options) } contains(inDate: Date): boolean { const offset = offsetWithTimeZone(this.zone, inDate) const scheduleZoneDate = new Date(inDate.getTime() + offset) const exception = this.exceptions.find(exception => isSameDay(scheduleZoneDate, exception.date) ) if (exception) { return exception.daySchedule.contains(scheduleZoneDate) } const weekDay = scheduleZoneDate.getDay() as WeekDay const schedule = this.scheduleByDay.get(weekDay) if (!schedule) { return false } return schedule.contains(scheduleZoneDate) } } export class ScheduleAlwaysOn extends Schedule { constructor() { super('UTC') } contains(_date: Date): boolean { return true } } export class DaySchedule { constructor(readonly ranges: TimeRange[]) {} contains(date: Date): boolean { for (const range of this.ranges) { if (range.contains(date)) { return true } } return false } } export enum WeekDay { SUNDAY = 0, // compatible with Date getDay MONDAY = 1, TUESDAY = 2, WEDNESDAY = 3, THURSDAY = 4, FRIDAY = 5, SATURDAY = 6, } class ExceptionSchedule { constructor( readonly date: Date, readonly daySchedule: DaySchedule ) {} } export class TimeRange { readonly from: HourAndMinute readonly to: HourAndMinute /** * @param from inclusive * @param to exclusive if 00:00, it will be interpreted as 24:00 */ constructor(from: HourAndMinute, to: HourAndMinute) { this.from = from this.to = to.isMidnight() ? new HourAndMinute(24, 0) : to if (this.from.compare(this.to) >= 0) { throw new Error(`${from.toString()} should be before ${to.toString()}`) } } contains(date: Date): boolean { if (this.from.compareToDate(date) > 0) { return false } return this.to.compareToDate(date) > 0 } } export class HourAndMinute { constructor( readonly hour: number, readonly minute: number = 0 ) {} compareToDate(date: Date): number { return HourAndMinute.compareNumber( this.toMinutes(), date.getHours() * 60 + date.getMinutes() ) } isMidnight(): boolean { return this.hour === 0 && this.minute === 0 } private static compareNumber(first: number, second: number): number { if (first === second) { return 0 } if (first < second) { return -1 } return 1 } compare(other: HourAndMinute): number { return HourAndMinute.compareNumber(this.toMinutes(), other.toMinutes()) } private toMinutes(): number { return this.hour * 60 + this.minute } toString(): string { let str = this.hour.toString() if (this.minute != 0) { str += ':' + this.minute.toString() } return str + 'h' } } // BUG should check date in schedule timezone function isSameDay(d1: Date, d2: Date): boolean { return ( d1.getFullYear() === d2.getFullYear() && d1.getMonth() === d2.getMonth() && d1.getDate() === d2.getDate() ) }