date.ts
1 import dayjs, { UnitType } from "dayjs" 2 3 import dayjsDurationPlugin from "dayjs/plugin/duration" 4 import dayjsAdvancedFormatPlugin from "dayjs/plugin/advancedFormat" 5 import dayjsIsoWeekPlugin from "dayjs/plugin/isoWeek" 6 import dayjsWeekYearPlugin from "dayjs/plugin/weekYear" 7 import dayjsWeekOfYearPlugin from "dayjs/plugin/weekOfYear" 8 import dayjsRelativeTimePlugin from "dayjs/plugin/relativeTime" 9 import dayjsUtcPlugin from "dayjs/plugin/utc" 10 import dayjsTimezonePlugin from "dayjs/plugin/timezone" 11 12 dayjs.extend(dayjsDurationPlugin) 13 dayjs.extend(dayjsAdvancedFormatPlugin) 14 dayjs.extend(dayjsIsoWeekPlugin) 15 dayjs.extend(dayjsWeekYearPlugin) 16 dayjs.extend(dayjsWeekOfYearPlugin) 17 dayjs.extend(dayjsRelativeTimePlugin) 18 dayjs.extend(dayjsUtcPlugin) 19 dayjs.extend(dayjsTimezonePlugin) 20 21 /** 22 * This file was largely taken from the helper-date package - we did this for two reasons: 23 * 1. It made use of both moment of date.js - this caused some weird bugs with some relatively simple 24 * syntax and didn't offer much in return. 25 * 2. Replacing moment with dayjs helps massively reduce bundle size. 26 * The original package can be found here: 27 * https://github.com/helpers/helper-date 28 */ 29 30 function isOptions(val: any) { 31 return typeof val === "object" && typeof val.hash === "object" 32 } 33 34 function isApp(thisArg: any) { 35 return ( 36 typeof thisArg === "object" && 37 typeof thisArg.options === "object" && 38 typeof thisArg.app === "object" 39 ) 40 } 41 42 function getContext(thisArg: any, locals: any, options: any) { 43 if (isOptions(thisArg)) { 44 return getContext({}, locals, thisArg) 45 } 46 // ensure args are in the correct order 47 if (isOptions(locals)) { 48 return getContext(thisArg, options, locals) 49 } 50 const appContext = isApp(thisArg) ? thisArg.context : {} 51 options = options || {} 52 53 // if "options" is not handlebars options, merge it onto locals 54 if (!isOptions(options)) { 55 locals = Object.assign({}, locals, options) 56 } 57 // merge handlebars root data onto locals if specified on the hash 58 if (isOptions(options) && options.hash.root === true) { 59 locals = Object.assign({}, options.data.root, locals) 60 } 61 let context = Object.assign({}, appContext, locals, options.hash) 62 if (!isApp(thisArg)) { 63 context = Object.assign({}, thisArg, context) 64 } 65 if (isApp(thisArg) && thisArg.view && thisArg.view.data) { 66 context = Object.assign({}, context, thisArg.view.data) 67 } 68 return context 69 } 70 71 function initialConfig(str: any, pattern: any, options?: any) { 72 if (isOptions(pattern)) { 73 options = pattern 74 pattern = DEFAULT_FORMAT 75 } 76 77 if (isOptions(str)) { 78 options = str 79 pattern = null 80 str = null 81 } 82 return { str, pattern, options } 83 } 84 85 function setLocale(this: any, str: any, pattern: any, options?: any) { 86 // if options is null then it'll get updated here 87 const config = initialConfig(str, pattern, options) 88 const defaults = { lang: "en", date: new Date(config.str) } 89 // for now don't allow this to be configurable, don't pass in options 90 const opts = getContext(this, defaults, {}) 91 92 // set the language to use 93 dayjs.locale(opts.lang || opts.language) 94 } 95 96 const DEFAULT_FORMAT = "MMMM DD, YYYY" 97 98 export const date = (str: any, pattern: any, options: any) => { 99 const config = initialConfig(str, pattern, options) 100 101 // if no args are passed, return a formatted date 102 if (config.str == null && config.pattern == null) { 103 dayjs.locale("en") 104 return dayjs().format(DEFAULT_FORMAT) 105 } 106 107 setLocale(config.str, config.pattern, config.options) 108 109 let date = dayjs(new Date(config.str)) 110 if (typeof config.options === "string") { 111 date = 112 config.options.toLowerCase() === "utc" 113 ? date.utc() 114 : date.tz(config.options) 115 } else { 116 date = date.tz(dayjs.tz.guess()) 117 } 118 if (config.pattern === "") { 119 return date.toISOString() 120 } 121 return date.format(config.pattern) 122 } 123 124 export const duration = (str: any, pattern: any, format?: any) => { 125 const config = initialConfig(str, pattern) 126 127 setLocale(config.str, config.pattern) 128 129 const duration = dayjs.duration(config.str, config.pattern) 130 if (format && !isOptions(format)) { 131 return duration.format(format) 132 } else { 133 return duration.humanize() 134 } 135 } 136 137 export const difference = (from: string, to: string, units?: UnitType) => { 138 const result = dayjs(new Date(from)).diff(dayjs(new Date(to)), units) 139 return result 140 } 141 142 export const durationFromNow = (from: string) => { 143 const diff = difference(from, new Date().toISOString(), "ms") 144 return duration(diff, "ms") 145 }