Skip to content

Instantly share code, notes, and snippets.

@scorredoira
Created August 15, 2020 16:33
Show Gist options
  • Save scorredoira/cf7caa1b0f347a0d33531f67e3b757e1 to your computer and use it in GitHub Desktop.
Save scorredoira/cf7caa1b0f347a0d33531f67e3b757e1 to your computer and use it in GitHub Desktop.
namespace S {
let formats: any = {}
let systemLocation = Intl.DateTimeFormat().resolvedOptions().timeZone
export class Date {
private _date: globalThis.Date
private _location: string
private _locationOffset: number
constructor(d?: number, location?: string) {
this._date = d ? new globalThis.Date(d) : new globalThis.Date()
if (location) {
this._location = location
this._locationOffset = this.getTimezoneOffset(this, location)
} else {
this._location = systemLocation
this._locationOffset = this._date.getTimezoneOffset()
}
}
get location() {
return this._location
}
get locationOffset() {
return this._locationOffset
}
getTime() {
return this._date.getTime()
}
getHours() {
return this._date.getHours()
}
getMinutes() {
return this._date.getMinutes()
}
getSeconds() {
return this._date.getSeconds()
}
getMilliseconds() {
return this._date.getMilliseconds()
}
getDate() {
return this._date.getDate()
}
getDay() {
return this._date.getDay()
}
getMonth() {
return this._date.getMonth()
}
getFullYear() {
return this._date.getFullYear()
}
isSameDay(d: Date) {
return this.getFullYear() === d.getFullYear()
&& this.getMonth() === d.getMonth()
&& this.getDate() === d.getDate()
}
addMilliseconds(millis: number) {
return new Date(this.getTime() + millis, this._location)
}
addMinutes(minutes: number) {
return new Date(this.getTime() + minutes * 60000, this._location)
}
addHours(hours: number) {
return new Date(this.getTime() + hours * 60 * 60000, this._location)
}
addDays(days: number) {
var d = new Date(this.getTime(), this._location)
d._date.setDate(d._date.getDate() + days)
return d;
}
setDay(day: number) {
var d = new Date(this.getTime(), this._location)
d._date.setDate(day)
return d
}
in(location: string) {
let utc = this.toUTC()
let offset = this.getTimezoneOffset(this, location)
let dl = new Date(utc - offset * 60 * 1000, location)
return dl
}
static newDate(year?: number, month?: number, day?: number, hour?: number, min?: number, secs?: number, millis?: number, location?: string) {
let d = new globalThis.Date(year || 1, month || 1, day || 1, hour || 0, min || 0, secs || 0, millis || 0)
return new Date(d.getTime(), location)
}
startOfDay() {
return Date.newDate(this.getFullYear(), this.getMonth(), this.getDate(), null, null, null, null, this._location)
}
endOfDay() {
let d = Date.newDate(this.getFullYear(), this.getMonth(), this.getDate(), null, null, null, null, this._location)
d._date.setDate(d._date.getDate() + 1)
return new Date(d.getTime() - 1, this.location)
}
copy() {
return new Date(this.getTime(), this.location)
}
toUTC() {
return this._date.getTime() + this._locationOffset * 60 * 1000
}
private getTimezoneOffset(d: Date, location: string) {
const utcStr = this.formatTimezone("UTC", d)
const localStr = this.formatTimezone(location, d)
let localISO = this.parseISOLocal(localStr)
let utcISO = this.parseISOLocal(utcStr)
let offset = utcISO.getTime() - localISO.getTime()
return offset / 60 / 1000
}
private formatTimezone(timeZone: string, d: Date) {
let f = this.getFormat(timeZone)
let s = f.format(d._date)
// handle Chrome80 bug: goes from 23:59:59 to 24:00:00
var parts = s.splitEx(/\D/)
let hour = parseInt(parts[3], 10)
if (hour >= 24) {
hour -= 24
s = parts[0] + "/" + parts[1] + "/" + parts[2] + ", " + hour + ":" + parts[4] + ":" + parts[5]
}
return s
}
toString() {
return this.format("yyyy/MM/dd HH:mm") + " (" + this._location + ")"
}
format(pattern: string) {
let parts: any[] = []
for (let i = 0, l = pattern.length - 1; i <= l; i++) {
let c = pattern[i]
switch (c) {
case "d":
if (i < l && pattern[i + 1] == "d") {
if (i < l - 1 && pattern[i + 2] == "d") {
parts.push({ token: "ddd" })
i += 2
continue
}
parts.push({ token: "dd" })
i++
continue
}
parts.push({ token: "d" })
continue
case "M":
if (i < l && pattern[i + 1] == "M") {
parts.push({ token: "MM" })
i++
continue
}
parts.push({ token: "M" })
continue
case "y":
if (i < l && pattern[i + 1] == "y") {
if (i < l - 2 && pattern[i + 2] == "y" && pattern[i + 3] == "y") {
parts.push({ token: "yyyy" })
i += 3
continue
}
parts.push({ token: "yy" })
i++
continue
}
parts.push(c)
break
case "h":
if (i < l && pattern[i + 1] == "h") {
parts.push({ token: "hh" })
i++
continue
}
parts.push({ token: "h" })
continue
case "H":
if (i < l && pattern[i + 1] == "H") {
parts.push({ token: "HH" })
i++
continue
}
parts.push({ token: "H" })
continue
case "m":
if (i < l && pattern[i + 1] == "m") {
if (i < l - 1 && pattern[i + 2] == "m") {
parts.push({ token: "mmm" })
i += 2
continue
}
parts.push({ token: "mm" })
i++
continue
}
parts.push({ token: "m" })
continue
case "s":
if (i < l && pattern[i + 1] == "s") {
parts.push({ token: "ss" })
i++
continue
}
parts.push({ token: "s" })
continue
case "z":
if (i < l && pattern[i + 1] == "z") {
parts.push({ token: "zz" })
i++
continue
}
parts.push(c)
continue
case "\\":
if (i < l) {
parts.push(pattern[i + 1])
i++
continue
}
break
default:
parts.push(c)
break
}
}
let result = []
for (let part of parts) {
switch (part.token) {
case "d":
result.push(this.getDate())
break
case "dd":
result.push(padLeft(String(this.getDate()), "0", 2))
break
case "ddd":
let day = weekDays[this.getDay()]
result.push(day)
break
case "M":
result.push(this.getMonth())
break
case "MM":
result.push(padLeft(String(this.getMonth()), "0", 2))
break
case "mmm":
let month = monthNames[this.getMonth()]
result.push(month)
break
case "yy":
result.push(padLeft(String(this.getFullYear() - 2000), "0", 2))
break
case "yyyy":
result.push(this.getFullYear())
break
case "h":
result.push(this.getHours())
break
case "hh":
let hours = this.getHours()
if (hours > 12) {
hours -= 12
}
result.push(padLeft(String(hours), "0", 2))
break
case "HH":
result.push(padLeft(String(this.getHours()), "0", 2))
break
case "m":
result.push(this.getMinutes())
break
case "mm":
result.push(padLeft(String(this.getMinutes()), "0", 2))
break
case "s":
result.push(this.getSeconds())
break
case "ss":
result.push(padLeft(String(this.getSeconds()), "0", 2))
break
case "zz":
result.push(this._location)
break
case undefined:
result.push(part)
break
default:
result.push(part.token)
break
}
}
return result.join("")
}
private getFormat(timeZone: string) {
let f = formats[timeZone]
if (!f) {
// 09/18/2019, 13:49:27
f = new Intl.DateTimeFormat('en-US', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false,
timeZone
})
formats[timeZone] = f
}
return f
}
private parseISOLocal(s: string) {
var parts = s.splitEx(/\D/)
if (parts.length != 6) {
throw "Invalid ISO date"
}
let month = Number(parts[0]) - 1
let day = Number(parts[1])
let year = Number(parts[2])
let hour = Number(parts[3])
let min = Number(parts[4])
let sec = Number(parts[5])
return new globalThis.Date(year, month, day, hour, min, sec, 0)
}
}
export var weekDays = ["@@Sunday", "@@Monday", "@@Tuesday", "@@Wednesday", "@@Thursday", "@@Friday", "@@Saturday"]
export function getWeekdayName(dayOfWeek: number) {
return weekDays[dayOfWeek]
}
let monthNames = [
"@@January",
"@@February",
"@@March",
"@@April",
"@@May",
"@@Jun",
"@@Julay",
"@@August",
"@@Sepember",
"@@October",
"@@November",
"@@December",
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment