-
Notifications
You must be signed in to change notification settings - Fork 3
/
birthday_template.js
154 lines (136 loc) · 5 KB
/
birthday_template.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/* Reminder: Nightbot cannot work with inline comments, use block comments only */
const birthdayMonth = 1; /* 1 = January. 12 = December, etc... */
const birthdayDay = 1; /* 1 - 28, 30, 31, etc... */
const timeZone = "America/Los_Angeles"; /* IANA Time Zone name */
const secondInMs = 1000;
const minuteInMs = 60 * secondInMs;
const hourInMs = 60 * minuteInMs;
const dayInMs = 24 * hourInMs;
/**
* findTimeZoneOffsetMs returns the timezone offset in milliseconds of the specified time zone
* @param {string} timeZone
* @returns {number}
*/
function findTimeZoneOffsetMs(timeZone) {
const offsetFormat = new Intl.DateTimeFormat("en-US", {
timeZone: timeZone,
hourCycle: "h23", /* this prevents Intl from presenting 00:00 as 24:00 */
month: "numeric",
day: "numeric",
year: "numeric",
hour: "numeric",
minute: "numeric",
second: "numeric",
});
const offsetTimeStr = offsetFormat.format(0);
const extractRegex = /(\d{1,2})\/(\d{1,2})\/(\d{4}), (\d{2}):(\d{2}):(\d{2})/;
const offsetFrags = offsetTimeStr.match(extractRegex);
/* this is in milliseconds, months are zero indexed */
return Date.UTC(offsetFrags[3], offsetFrags[1] - 1, offsetFrags[2], offsetFrags[4], offsetFrags[5], offsetFrags[6]);
}
/**
* getCurrentTime returns a Date object of the current moment in time, adjusted with the time zone offset
* @param {number} offset
* @returns {Date}
*/
function getCurrentTime(offset) {
const now = new Date();
now.setUTCMilliseconds(now.getUTCMilliseconds() + offset);
return now;
}
/**
* findNextBirthday finds the next possible birthday date, represented as a Date object
* @param {number} month
* @param {number} day
* @param {number} offset
* @returns {Date}
*/
function findNextBirthday(month, day, now) {
const birthday = new Date();
birthday.setUTCDate(1);
birthday.setUTCMonth(month - 1);
birthday.setUTCDate(day);
birthday.setUTCHours(0, 0, 0, 0);
const dayAfterBirthday = new Date(birthday);
dayAfterBirthday.setUTCHours(24);
if (dayAfterBirthday <= now) {
/* this birthday is in the past, increment the year */
birthday.setUTCFullYear(birthday.getUTCFullYear() + 1);
}
return birthday;
}
/**
* getMsUntilBirthday returns the milliseconds until the birthday begins
* @param {Date} birthday
* @param {number} offset
* @returns {number}
*/
function getMsUntilBirthday(birthday, now) {
return now.valueOf() - birthday.valueOf();
}
/**
* buildTimeDiff takes a number in milliseconds and converts it into bigger units of time
* @param {number} diff
* @returns {Array}
*/
function buildTimeDiff(diff) {
const isBirthdayToday = diff >= 0;
/* use absolute value here so i can floor divide without worrying about ceilings for negatives */
let t = Math.abs(diff);
const days = Math.floor(t / dayInMs);
t = t % dayInMs;
const hours = Math.floor(t / hourInMs);
t = t % hourInMs;
const minutes = Math.floor(t / minuteInMs);
t = t % minuteInMs;
const seconds = Math.floor(t / secondInMs);
return [isBirthdayToday, days, hours, minutes, seconds];
}
/**
* formatTimeDiffFrags presents units of time as a string
* @param {boolean} isBirthdayToday
* @param {number} days
* @param {number} hours
* @param {number} minutes
* @param {number} seconds
* @returns {string}
*/
function formatTimeDiffFrags(isBirthdayToday, days, hours, minutes, seconds) {
if ((days + hours + minutes + seconds) === 0) {
return "FeelsBirthdayMan";
}
const strFrags = [];
/* if the birthday is today, then days should be 0 anyway */
if (days > 0) {
const unit = days > 1 ? "days" : "day";
strFrags.push(`${days} ${unit}`);
}
if (hours > 0) {
const unit = hours > 1 ? "hours" : "hour";
strFrags.push(`${isBirthdayToday ? 23 - hours : hours} ${unit}`);
}
if (minutes > 0) {
const unit = minutes > 1 ? "minutes" : "minute";
strFrags.push(`${isBirthdayToday ? 59 - minutes : minutes} ${unit}`);
}
if (seconds > 0) {
const unit = seconds > 1 ? "seconds" : "second";
strFrags.push(`${isBirthdayToday ? 59 - seconds : seconds} ${unit}`);
}
/* add "and" fragment to the final one to be fancy */
if (strFrags.length > 1) {
let lastFrag = strFrags.pop();
lastFrag = `and ${lastFrag}`;
strFrags.push(lastFrag);
}
const prefix = isBirthdayToday ? "Ends in " : "Begins in ";
const middle = strFrags.length > 2 ? strFrags.join(", ") : strFrags.join(" ");
const suffix = isBirthdayToday ? " FeelsBirthdayMan " : "";
return `${prefix}${middle}${suffix}`;
}
const timeZoneOffsetMs = findTimeZoneOffsetMs(timeZone);
const now = getCurrentTime(timeZoneOffsetMs);
const nextBirthday = findNextBirthday(birthdayMonth, birthdayDay, now);
const nextMs = getMsUntilBirthday(nextBirthday, now);
const [isBirthdayToday, days, hours, minutes, seconds] = buildTimeDiff(nextMs);
formatTimeDiffFrags(isBirthdayToday, days, hours, minutes, seconds);