time-values
Version:
Gets date/time attributes from a date or string with no dependencies
117 lines (94 loc) • 4.76 kB
JavaScript
const emptyObj = { year: 0, month: 0, day: 0, hour: 0, minute: 0, second: 0 }
const stringNums = (start = 0, end = 0) => Array.from(Array(end - start + 1), (_, i) => `${i + start}`)
const getTimeValues = (inputDate = new Date()) => {
if (!inputDate) {
return { ...emptyObj }
}
// Assume this is a time-values output object, and fill in the blanks
if (!(inputDate instanceof Date) && inputDate instanceof Object) {
let year = inputDate.year == null ? new Date().getFullYear() : +inputDate.year
let month = inputDate.month == null ? new Date().getMonth() + 1 : +inputDate.month
let day = inputDate.day == null ? new Date().getDate() : +inputDate.day
let hour = inputDate.hour == null ? new Date().getHours() : +inputDate.hour
let minute = inputDate.minute == null ? new Date().getMinutes() : +inputDate.minute
let second = inputDate.second == null ? new Date().getSeconds() : +inputDate.second
// Loop around values out of range
while (second >= 60) { second -= 60; minute++ }
while (second < 0) { second += 60; minute-- }
while (minute >= 60) { minute -= 60; hour++ }
while (minute < 0) { minute += 60; hour-- }
while (hour >= 24) { hour -= 24; day++ }
while (hour < 0) { hour += 24; day-- }
while (month > 12) { month -= 12; year++ }
while (month < 1) { month += 12; year-- }
// days are trickier because they're based off the days in a given month
while (day > new Date(year, month, 0).getDate()) {
day -= new Date(year, month, 0).getDate()
month++
while (month > 12) { month -= 12; year++ }
}
while (day < 1) {
month--
while (month < 1) { month += 12; year-- }
day += new Date(year, month, 0).getDate()
}
return { year, month, day, hour, minute, second }
}
// String dates often have weird, special handling
if (typeof inputDate === 'string') {
// mm/dd/yyyy dates are a little wonky because they are always considered local tz, not UTC.
if (/^\d{2}\/\d{2}\/\d{4}$/.test(inputDate)) {
const [month, day, year] = inputDate.split('/')
return { ...emptyObj, year: +year, month: +month, day: +day }
}
// ISO dates that don't specify a time should default to the start of the day
// (amusingly this only applies for properly formatted ISO dates, so 2022-2-2 is fine)
if (/^\d{4}\-\d{2}\-\d{2}$/.test(inputDate)) {
const [year, month, day] = inputDate.split('-')
return { ...emptyObj, year: +year, month: +month, day: +day }
}
// A year followed by a time value (separated by a 'T')
if (/^\d{4}T/.test(inputDate)) {
const [year, timeRaw] = inputDate.split('T')
const [hour, minute, second] = timeRaw.split('.')[0].split(':').map(n => +n)
return { year: +year, month: 1, day: 1, hour, minute, second }
}
// A year followed by a time value (separated by a ':')
if (/^\d{4}:/.test(inputDate)) {
const year = inputDate.substring(0, inputDate.indexOf(':'))
const timeRaw = inputDate.substring(inputDate.indexOf(':') + 1)
const [hour, minute, second] = timeRaw.split('.')[0].split(':').map(n => +n)
return { year: +year, month: 1, day: 1, hour, minute, second }
}
// yyyy-mm with no day format defaults to the first of the month at hour 0
if (/^\d{4}-\d{2}$/.test(inputDate)) {
const year = inputDate.substring(0, inputDate.indexOf('-'))
const month = inputDate.substring(inputDate.indexOf('-') + 1)
return { year: +year, month: +month, day: 1, hour: 0, minute: 0, second: 0 }
}
// When just 1-4 digits are specified
if (/^\d?\d?\d?\d$/.test(inputDate)) {
// Yeah, this is a thing
if (inputDate === '0') {
return { ...emptyObj, year: 2000, month: 1, day: 1 }
}
// As is this
if (stringNums(1, 12).includes(inputDate) || stringNums(1, 12).map(s => `0${s}`).includes(inputDate)) {
return { ...emptyObj, year: 2001, month: +inputDate, day: 1 }
}
// Anything else that is just up to 4 digits is a year
return { ...emptyObj, year: +inputDate, month: 1, day: 1}
}
}
const date = new Date(new Date(inputDate).getTime() - new Date(inputDate).getTimezoneOffset() * 6e4)
const [dateRaw, timeRaw] = date.toISOString().split('T')
const [year, month, day] = dateRaw.split('-').map(v => parseInt(v, 10))
const [hour, minute, second] = timeRaw.split('.')[0].split(':').map(n => +n)
return { year, month: month, day, hour, minute, second }
}
getTimeValues.toDate = (timeValues) => {
const p = s => `${s}`.padStart(2, '0')
const t = getTimeValues(timeValues)
return new Date(`${t.year}-${p(t.month)}-${p(t.day)}T${p(t.hour)}:${p(t.minute)}:${p(t.second)}`)
}
module.exports = getTimeValues