0, 2, 3, 4 /* 2012, 2013, 2014, 2015 */
};
+/* Let's assume people are going to be looking for dates in the future.
+ Let's provide some cheats so you can skip ahead.
+ This has a 4x speed boost when near 2008.
+*/
+/* Number of days since epoch on Jan 1st, 2008 GMT */
+#define CHEAT_DAYS (1199145600 / 24 / 60 / 60)
+#define CHEAT_YEARS 108
#define IS_LEAP(n) ((!(((n) + 1900) % 400) || (!(((n) + 1900) % 4) && (((n) + 1900) % 100))) != 0)
#define WRAP(a,b,m) ((a) = ((a) < 0 ) ? ((b)--, (a) + (m)) : (a))
-int _is_exception_century(long year)
+int _is_exception_century(Int64 year)
{
int is_exception = ((year % 100 == 0) && !(year % 400 == 0));
/* printf("is_exception_century: %s\n", is_exception ? "yes" : "no"); */
return(is_exception);
}
-void _check_tm(struct tm *tm)
-{
- /* Don't forget leap seconds */
- assert(tm->tm_sec >= 0 && tm->tm_sec <= 61);
- assert(tm->tm_min >= 0 && tm->tm_min <= 59);
- assert(tm->tm_hour >= 0 && tm->tm_hour <= 23);
- assert(tm->tm_mday >= 1 && tm->tm_mday <= 31);
- assert(tm->tm_mon >= 0 && tm->tm_mon <= 11);
- assert(tm->tm_wday >= 0 && tm->tm_wday <= 6);
- assert(tm->tm_yday >= 0 && tm->tm_yday <= 365);
-
-#ifdef TM_HAS_GMTOFF
- assert( tm->tm_gmtoff >= -24 * 60 * 60
- && tm->tm_gmtoff <= 24 * 60 * 60);
+
+/* timegm() is a GNU extension, so emulate it here if we need it */
+#ifdef HAS_TIMEGM
+# define TIMEGM(n) timegm(n);
+#else
+# define TIMEGM(n) _my_timegm(n);
#endif
- if( !IS_LEAP(tm->tm_year) ) {
- /* no more than 365 days in a non_leap year */
- assert( tm->tm_yday <= 364 );
+time_t _my_timegm(struct tm *date) {
+ int days = 0;
+ int seconds = 0;
+ time_t time;
+ int year;
- /* and no more than 28 days in Feb */
- if( tm->tm_mon == 1 ) {
- assert( tm->tm_mday <= 28 );
+ if( date->tm_year > 70 ) {
+ year = 70;
+ while( year < date->tm_year ) {
+ days += length_of_year[IS_LEAP(year)];
+ year++;
}
}
+ else if ( date->tm_year < 70 ) {
+ year = 69;
+ do {
+ days -= length_of_year[IS_LEAP(year)];
+ year--;
+ } while( year >= date->tm_year );
+ }
+
+ days += julian_days_by_month[IS_LEAP(date->tm_year)][date->tm_mon];
+ days += date->tm_mday - 1;
+
+ seconds += date->tm_hour * 60 * 60;
+ seconds += date->tm_min * 60;
+ seconds += date->tm_sec;
+
+ time = (time_t)(days * 60 * 60 * 24) + seconds;
+
+ return(time);
+}
+
+
+void _check_tm(struct tm *tm)
+{
+ int is_leap = IS_LEAP(tm->tm_year);
+
+ /* Don't forget leap seconds */
+ assert(tm->tm_sec >= 0);
+ assert(tm->tm_sec <= 61);
+
+ assert(tm->tm_min >= 0);
+ assert(tm->tm_min <= 59);
+
+ assert(tm->tm_hour >= 0);
+ assert(tm->tm_hour <= 23);
+
+ assert(tm->tm_mday >= 1);
+ assert(tm->tm_mday <= days_in_month[is_leap][tm->tm_mon]);
+
+ assert(tm->tm_mon >= 0);
+ assert(tm->tm_mon <= 11);
+
+ assert(tm->tm_wday >= 0);
+ assert(tm->tm_wday <= 6);
+
+ assert(tm->tm_yday >= 0);
+ assert(tm->tm_yday <= length_of_year[is_leap]);
+
+#ifdef HAS_TM_TM_GMTOFF
+ assert(tm->tm_gmtoff >= -24 * 60 * 60);
+ assert(tm->tm_gmtoff <= 24 * 60 * 60);
+#endif
}
/* The exceptional centuries without leap years cause the cycle to
shift by 16
*/
-int _cycle_offset(long year)
+int _cycle_offset(Int64 year)
{
- const long start_year = 2000;
- long year_diff = year - start_year - 1;
- long exceptions = year_diff / 100;
+ const Int64 start_year = 2000;
+ Int64 year_diff = year - start_year - 1;
+ Int64 exceptions = year_diff / 100;
exceptions -= year_diff / 400;
assert( year >= 2001 );
year in the 28 year calendar cycle.
*/
#define SOLAR_CYCLE_LENGTH 28
-int _safe_year(long year)
+int _safe_year(Int64 year)
{
int safe_year;
- long year_cycle = year + _cycle_offset(year);
+ Int64 year_cycle = year + _cycle_offset(year);
/* Change non-leap xx00 years to an equivalent */
if( _is_exception_century(year) )
struct tm *gmtime64_r (const Time64_T *in_time, struct tm *p)
{
int v_tm_sec, v_tm_min, v_tm_hour, v_tm_mon, v_tm_wday;
- Time64_T v_tm_tday;
+ Int64 v_tm_tday;
int leap;
- Time64_T m;
+ Int64 m;
Time64_T time = *in_time;
- Time64_T year;
+ Int64 year = 70;
-#ifdef TM_HAS_GMTOFF
+#ifdef HAS_TM_TM_GMTOFF
p->tm_gmtoff = 0;
#endif
p->tm_isdst = 0;
-#ifdef TM_HAS_ZONE
+#ifdef HAS_TM_TM_ZONE
p->tm_zone = "UTC";
#endif
if ((v_tm_wday = (v_tm_tday + 4) % 7) < 0)
v_tm_wday += 7;
m = v_tm_tday;
- if (m >= 0) {
- year = 70;
+ if (m >= CHEAT_DAYS) {
+ year = CHEAT_YEARS;
+ m -= CHEAT_DAYS;
+ }
+
+ if (m >= 0) {
/* Gregorian cycles, this is huge optimization for distant times */
while (m >= (Time64_T) days_in_gregorian_cycle) {
m -= (Time64_T) days_in_gregorian_cycle;
v_tm_mon++;
}
} else {
- year = 69;
+ year--;
/* Gregorian cycles */
while (m < (Time64_T) -days_in_gregorian_cycle) {
p->tm_year = year;
if( p->tm_year != year ) {
+#ifdef EOVERFLOW
errno = EOVERFLOW;
+#endif
return NULL;
}
{
time_t safe_time;
struct tm gm_tm;
- long orig_year;
+ Int64 orig_year;
int month_diff;
gmtime64_r(time, &gm_tm);
if (gm_tm.tm_year > (2037 - 1900))
gm_tm.tm_year = _safe_year(gm_tm.tm_year + 1900) - 1900;
- safe_time = timegm(&gm_tm);
+ safe_time = TIMEGM(&gm_tm);
localtime_r(&safe_time, local_tm);
local_tm->tm_year = orig_year;