Skip to content

Instantly share code, notes, and snippets.

@mscalora
Last active September 15, 2025 02:17
Show Gist options
  • Select an option

  • Save mscalora/838664c850c661099e2e0c3f6ed41c68 to your computer and use it in GitHub Desktop.

Select an option

Save mscalora/838664c850c661099e2e0c3f6ed41c68 to your computer and use it in GitHub Desktop.
A very small library of north american centric date/time utilities
#define ATLANTIC_TZ -5
#define EASTERN_TZ -5
#define CENTRAL_TZ -6
#define MOUNTAIN_TZ -7
#define PACIFIC_TZ -8
#define ALASKA_TZ -9
#define HAWAII_TZ -11
struct DateTime {
int year;
int month;
int day;
int hour;
int hour12;
int min;
int sec;
bool pm;
};
bool is_leap(int year) {
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}
int day_of_week(int year, int month, int day) {
if (month < 3) {
month += 12;
year -= 1;
}
int y = year % 100;
int c = year / 100;
int day_of_week_val = (day + (13 * (month + 1)) / 5 + y + y / 4 + c / 4 + 5 * c) % 7;
return (day_of_week_val + 6) % 7;
}
int nth_weekday_date(int year, int month, int n, int weekday) {
int first_day_weekday = day_of_week(year, month, 1);
int diff = weekday - first_day_weekday;
if (diff < 0) {
diff += 7;
}
return 1 + diff + (n - 1) * 7;
}
bool is_daylight_savings(int year, int month, int day) {
if (month < 3 || month > 11) {
return false;
}
if (month > 3 && month < 11) {
return true;
}
if (month == 3) {
int dst_start_day = nth_weekday_date(year, 3, 2, 0);
return day >= dst_start_day;
}
if (month == 11) {
int dst_end_day = nth_weekday_date(year, 11, 1, 0);
return day < dst_end_day;
}
return false;
}
void date_from_epoch(int64_t epoch_seconds, int& year, int& month, int& day) {
const int64_t SECONDS_IN_DAY = 86400;
int64_t total_days = epoch_seconds / SECONDS_IN_DAY;
year = 1970;
int64_t days_in_year;
while (true) {
days_in_year = is_leap(year) ? 366 : 365;
if (total_days < days_in_year) {
break;
}
total_days -= days_in_year;
year++;
}
int days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
if (is_leap(year)) {
days_in_month[1] = 29; // Adjust for February in a leap year
}
month = 1;
for (; month <= 12; ++month) {
if (total_days < days_in_month[month - 1]) {
break;
}
total_days -= days_in_month[month - 1];
}
day = static_cast<int>(total_days) + 1;
}
void datetime_from_epoch(int64_t epoch_seconds, DateTime& dt) {
const int64_t SECONDS_IN_DAY = 86400;
int64_t total_days = epoch_seconds / SECONDS_IN_DAY;
unsigned long secs = epoch_seconds % SECONDS_IN_DAY;
int year = 1970;
int64_t days_in_year;
while (true) {
days_in_year = is_leap(year) ? 366 : 365;
if (total_days < days_in_year) {
break;
}
total_days -= days_in_year;
year++;
}
int days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
if (is_leap(year)) {
days_in_month[1] = 29; // Adjust for February in a leap year
}
int month = 1;
for (; month <= 12; ++month) {
if (total_days < days_in_month[month - 1]) {
break;
}
total_days -= days_in_month[month - 1];
}
dt.year = year;
dt.month = month;
dt.day = static_cast<int>(total_days) + 1;
dt.hour = secs / (60*60);
dt.min = secs % (60*60) / 60;
dt.sec = secs % 60;
dt.pm = dt.hour>=12;
dt.hour12 = dt.hour;
if (dt.hour==0) {
dt.hour12 = 12;
} else if (dt.hour>12) {
dt.hour12 -= 12;
}
}
int tz_offset(unsigned long gmtEpoch, int tz = MOUNTAIN_TZ, bool dst = true) {
int year, month, day, offset = tz*60*60;
date_from_epoch(gmtEpoch + offset, year, month, day);
if (dst && is_daylight_savings(year, month, day)) {
offset += 60*60;
}
return offset;
}
String formatted_date_time(unsigned long epoch, bool date = true, bool time = true, bool time12 = true, bool sec = false) {
char buf[30];
int count = 0;
DateTime dt;
buf[count] = 0;
datetime_from_epoch(epoch, dt);
if (date) {
count += snprintf(buf+count, sizeof(buf) - count, "%4d/%02d/%02d", dt.year, dt.month, dt.day);
}
if (date && time) {
buf[count++] = ' ';
buf[count] = 0;
}
if (time) {
count += snprintf(buf+count, sizeof(buf) - count, "%d:%02d", time12 ? dt.hour12 : dt.hour, dt.min);
}
if (time && sec) {
count += snprintf(buf+count, sizeof(buf) - count, ":%02d", dt.sec);
}
if (time && time12) {
count += snprintf(buf+count, sizeof(buf) - count, "%s", dt.pm ? "pm" : "am");
}
return String(buf);
}
String formatted_date(unsigned long epoch) {
return formatted_date_time(epoch, true, false);
}
String formatted_time(unsigned long epoch, bool time12 = true, bool sec = false) {
return formatted_date_time(epoch, false, true, time12, sec);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment