/* * posix_time.c — minimal POSIX implementation on top of * getdatetime() (ESTEX SYSTIME $21). * * SDCC's z80.lib bundles time/localtime/mktime AND _RtcRead in a * single time.rel module, so the user can't override _RtcRead from a * separate object — overriding triggers a "multiple definition" * linker error. We sidestep that by implementing the whole POSIX time * API ourselves; the linker then never pulls SDCC's time.rel. * * The epoch is Unix (1970-01-01 00:00:00). No timezone support — * gmtime and localtime are identical. No DST. */ #include #include static const unsigned char mdays[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 }; static const char *const dnames[7] = { "Sun","Mon","Tue","Wed","Thu","Fri","Sat" }; static const char *const mnames[12] = { "Jan","Feb","Mar","Apr","May","Jun", "Jul","Aug","Sep","Oct","Nov","Dec" }; static int is_leap(unsigned int y) { return (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0); } /* Days elapsed from 1970-01-01 to (y, 1, 1). */ static unsigned long year_days(unsigned int y) { unsigned long d = 0; for (unsigned int i = 1970; i < y; i++) d += is_leap(i) ? 366 : 365; return d; } /* Days from Jan 1 to month start (1-based month input). */ static unsigned int month_days(unsigned int y, unsigned int m) { unsigned int d = 0; for (unsigned int i = 0; i < m - 1; i++) d += mdays[i]; if (m > 2 && is_leap(y)) d++; return d; } time_t time(time_t *t) { datetime_t dt; getdatetime(&dt); unsigned long days = year_days(dt.year) + month_days(dt.year, dt.month) + (dt.day - 1); time_t epoch = days * 86400UL + (unsigned long)dt.hour * 3600UL + (unsigned long)dt.minute * 60UL + dt.second; if (t) *t = epoch; return epoch; } /* localtime and gmtime share one static buffer — caller copies if * needed across further calls (matches POSIX behaviour). */ static struct tm tm_buf; struct tm *gmtime(time_t *timep) { unsigned long sec = *timep; tm_buf.tm_sec = (unsigned char)(sec % 60); sec /= 60; tm_buf.tm_min = (unsigned char)(sec % 60); sec /= 60; tm_buf.tm_hour = (unsigned char)(sec % 24); sec /= 24; /* sec is now days since 1970-01-01 (Thursday). */ tm_buf.tm_wday = (unsigned char)((4 + sec) % 7); /* find year */ unsigned int y = 1970; unsigned long days = sec; while (days >= (unsigned long)(is_leap(y) ? 366 : 365)) { days -= is_leap(y) ? 366 : 365; y++; } tm_buf.tm_year = (int)y - 1900; tm_buf.tm_yday = (int)days; /* find month/day */ unsigned int m = 0; while (m < 12) { unsigned int dim = mdays[m] + ((m == 1) && is_leap(y) ? 1u : 0u); if (days < dim) break; days -= dim; m++; } tm_buf.tm_mon = (unsigned char)m; tm_buf.tm_mday = (unsigned char)(days + 1); tm_buf.tm_isdst = 0; tm_buf.tm_hundredth = 0; return &tm_buf; } struct tm *localtime(time_t *timep) { return gmtime(timep); /* no timezone */ } time_t mktime(struct tm *tm) { unsigned int y = (unsigned int)(tm->tm_year + 1900); unsigned long days = year_days(y) + month_days(y, (unsigned int)tm->tm_mon + 1) + (unsigned int)(tm->tm_mday - 1); time_t epoch = days * 86400UL + (unsigned long)tm->tm_hour * 3600UL + (unsigned long)tm->tm_min * 60UL + tm->tm_sec; /* Backfill wday/yday so callers can inspect them. */ tm->tm_wday = (unsigned char)((4 + days) % 7); tm->tm_yday = (int)month_days(y, (unsigned int)tm->tm_mon + 1) + (tm->tm_mday - 1); return epoch; } /* "Day Mon DD HH:MM:SS YYYY\n" — 25 chars + NUL. */ static char asctime_buf[26]; char *asctime(struct tm *tm) { sprintf(asctime_buf, "%s %s %2d %02d:%02d:%02d %d\n", dnames[tm->tm_wday % 7], mnames[tm->tm_mon % 12], tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_year + 1900); return asctime_buf; } char *ctime(time_t *timep) { return asctime(localtime(timep)); }