/****************************************************************************** * Copyright © 2014-2016 The SuperNET Developers. * * * * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * * the top-level directory of this distribution for the individual copyright * * holder information and the developer policies on copyright and licensing. * * * * Unless otherwise agreed in a custom licensing agreement, no part of the * * SuperNET software, including this file may be copied, modified, propagated * * or distributed except according to the terms contained in the LICENSE file * * * * Removal or modification of this copyright notice is prohibited. * * * ******************************************************************************/ // DJB's libtai was modified for the tai code #include "OS_portable.h" #define DISABLE_LEAPS #define TAI_PACK 8 #define TAI_UTC_DIFF ((uint64_t)4611686018427387914ULL) //#define UTC_ADJUST -36 #define tai_approx(t) ((double) ((t)->x)) #define tai_less(t,u) ((t)->x < (u)->x) int32_t leapsecs_sub(struct tai *); static struct tai First_TAI; uint32_t First_utc; int32_t UTC_ADJUST; #ifdef _WIN32 struct tm *gmtime_r(const time_t *timep,struct tm *result) { struct tm *p = gmtime(timep); memset(result,0,sizeof(*result)); if ( p != 0 ) { *result = *p; p = result; } return(p); } struct tm *_gmtime32(const time_t *timep,struct tm *result) { return(gmtime_r(timep,result)); } time_t _time32(struct tm *tm) { return(time(NULL)); } time_t _localtime32(struct tm *tm) { return(time(NULL)); } #include #include // portable: uint64_t MSVC: __int64 // MSVC defines this in winsock2.h!? /*typedef struct timeval { long tv_sec; long tv_usec; } timeval; int gettimeofday(struct timeval * tp, struct timezone * tzp) { // Note: some broken versions only have 8 trailing zero's, the correct epoch has 9 trailing zero's static const uint64_t EPOCH = ((uint64_t) 116444736000000000ULL); SYSTEMTIME system_time; FILETIME file_time; uint64_t time; GetSystemTime( &system_time ); SystemTimeToFileTime( &system_time, &file_time ); time = ((uint64_t)file_time.dwLowDateTime ) ; time += ((uint64_t)file_time.dwHighDateTime) << 32; tp->tv_sec = (long) ((time - EPOCH) / 10000000L); tp->tv_usec = (long) (system_time.wMilliseconds * 1000); return 0; }*/ #endif double OS_portable_milliseconds() { struct timeval tv; double millis; gettimeofday(&tv,NULL); millis = ((double)tv.tv_sec * 1000. + (double)tv.tv_usec / 1000.); //printf("tv_sec.%ld usec.%d %f\n",tv.tv_sec,tv.tv_usec,millis); return(millis); } // portable time functions int32_t is_DST(int32_t datenum) { int32_t year,month,day; year = datenum / 10000, month = (datenum / 100) % 100, day = (datenum % 100); if ( month >= 4 && month <= 9 ) return(1); else if ( month == 3 && day >= 29 ) return(1); else if ( month == 10 && day < 25 ) return(1); return(0); } struct taidate taidate_set(int32_t year,int32_t month,int32_t day) { struct taidate cd; memset(&cd,0,sizeof(cd)); cd.year = year, cd.month = month, cd.day = day; return(cd); } struct taidate taidate_frommjd(int32_t day,int32_t *pwday,int32_t *pyday) { int32_t year,month,yday; struct taidate cd; year = day / 146097L; day %= 146097L; day += 678881L; while (day >= 146097L) { day -= 146097L; ++year; } // year * 146097 + day - 678881 is MJD; 0 <= day < 146097 // 2000-03-01, MJD 51604, is year 5, day 0 if ( pwday != 0 ) *pwday = (day + 3) % 7; year *= 4; if (day == 146096L) { year += 3; day = 36524L; } else { year += day / 36524L; day %= 36524L; } year *= 25; year += day / 1461; day %= 1461; year *= 4; yday = (day < 306); if (day == 1460) { year += 3; day = 365; } else { year += day / 365; day %= 365; } yday += day; day *= 10; month = (day + 5) / 306; day = (day + 5) % 306; day /= 10; if (month >= 10) { yday -= 306; ++year; month -= 10; } else { yday += 59; month += 2; } cd.year = year; cd.month = month + 1; cd.day = day + 1; if ( pyday != 0 ) *pyday = yday; return(cd); } struct taitime tai2time(struct tai t,int32_t *pwday,int32_t *pyday) { uint64_t u; int32_t leap,s; struct taitime ct; memset(&ct,0,sizeof(ct)); ct.millis = fmod(t.millis,999.999999); //if ( First_TAI.x != 0 && t.x > First_TAI.x ) // t.x = (t.millis / 1000.) + First_TAI.x; leap = leapsecs_sub(&t); u = t.x; u += 58486; // was off by a minute (or 3?) s = u % 86400ULL; ct.second = (s % 60) + leap; s /= 60; ct.minute = s % 60; s /= 60; ct.hour = s; u /= 86400ULL; ct.date = taidate_frommjd((int32_t)(u - 53375995543064ULL),pwday,pyday); ct.offset = 0; // printf("origt.x %llu t.x %llu %3.3f -> %02d:%02d:%02d %3.3f\n",(long long)origt.x,(long long)t.x,t.millis,ct.second,ct.minute,ct.second,ct.millis); //printf("TAI millis: %lld -1st.%lld %f - %f -> %f | %f\n",(long long)t.x,(long long)First_TAI.x,t.millis,First_TAI.millis,t.millis-First_TAI.millis,ct.millis); return(ct); } struct taidate tai2date(struct tai t) { struct taitime ct = tai2time(t,0,0); return(ct.date); } struct taitime taitime_set(struct taidate cd,int32_t hour,int32_t minute,int32_t seconds) { struct taitime ct; memset(&ct,0,sizeof(ct)); ct.date = cd; ct.hour = hour, ct.minute = minute, ct.second = seconds; return(ct); } /*int32_t taitime_scan(char *s,struct taitime *ct) { int32_t z,c,sign; char *t = s; t += taidate_scan(t,&ct->date); while ((*t == ' ') || (*t == '\t') || (*t == 'T')) ++t; z = 0; while ((c = (uint8_t) (*t - '0')) <= 9) { z = z * 10 + c; ++t; } ct->hour = z; if (*t++ != ':') return 0; z = 0; while ((c = (uint8_t) (*t - '0')) <= 9) { z = z * 10 + c; ++t; } ct->minute = z; if (*t != ':') ct->second = 0; else { ++t; z = 0; while ((c = (uint8_t) (*t - '0')) <= 9) { z = z * 10 + c; ++t; } ct->second = z; } while ((*t == ' ') || (*t == '\t')) ++t; if (*t == '+') sign = 1; else if (*t == '-') sign = -1; else return 0; ++t; c = (uint8_t) (*t++ - '0'); if (c > 9) return 0; z = c; c = (uint8_t) (*t++ - '0'); if (c > 9) return 0; z = z * 10 + c; c = (uint8_t) (*t++ - '0'); if (c > 9) return 0; z = z * 6 + c; c = (uint8_t) (*t++ - '0'); if (c > 9) return 0; z = z * 10 + c; ct->offset = z * sign; printf("t.%p s.%p\n",t,s); return((int32_t)((long)t - (long)s)); }*/ int32_t taidate_str(char *s,struct taidate cd) { int32_t x,len,i = 0; x = cd.year; if (x < 0) x = -x; do { ++i; x /= 10; } while(x); len = (cd.year < 0) + i + 6; if ( s != 0 ) { x = cd.year; if (x < 0) { x = -x; *s++ = '-'; } s += i; do { *--s = '0' + (x % 10); x /= 10; } while(x); s += i; x = cd.month; s[0] = '-'; s[2] = '0' + (x % 10); x /= 10; s[1] = '0' + (x % 10); x = cd.day; s[3] = '-'; s[5] = '0' + (x % 10); x /= 10; s[4] = '0' + (x % 10); s[len] = 0; } return(len); } char *taitime_str(char *s,struct taitime ct) { int32_t result,x,len; result = taidate_str(s,ct.date); len = result + 15; if ( s != 0 ) { s += result; x = ct.hour; s[0] = ' '; s[2] = '0' + (x % 10); x /= 10; s[1] = '0' + (x % 10); s += 3; x = ct.minute; s[0] = ':'; s[2] = '0' + (x % 10); x /= 10; s[1] = '0' + (x % 10); s += 3; x = ct.second; s[0] = ':'; s[2] = '0' + (x % 10); x /= 10; s[1] = '0' + (x % 10); s += 3; s[0] = ' '; x = ct.offset; if (x < 0) { s[1] = '-'; x = -x; } else s[1] = '+'; s[5] = '0' + (x % 10); x /= 10; s[4] = '0' + (x % 6); x /= 6; s[3] = '0' + (x % 10); x /= 10; s[2] = '0' + (x % 10); s[6] = 0; } return(s); } void tai_pack(char *s,struct tai *t) { uint64_t x; x = t->x; s[7] = x & 255; x >>= 8; s[6] = x & 255; x >>= 8; s[5] = x & 255; x >>= 8; s[4] = x & 255; x >>= 8; s[3] = x & 255; x >>= 8; s[2] = x & 255; x >>= 8; s[1] = x & 255; x >>= 8; s[0] = x; } void tai_unpack(char *s,struct tai *t) { uint64_t x; x = (uint8_t) s[0]; x <<= 8; x += (uint8_t) s[1]; x <<= 8; x += (uint8_t) s[2]; x <<= 8; x += (uint8_t) s[3]; x <<= 8; x += (uint8_t) s[4]; x <<= 8; x += (uint8_t) s[5]; x <<= 8; x += (uint8_t) s[6]; x <<= 8; x += (uint8_t) s[7]; t->x = x; } void tai_add(struct tai *t,struct tai *u,struct tai *v) { t->x = u->x + v->x; } void tai_sub(struct tai *t,struct tai *u,struct tai *v) { t->x = u->x - v->x; } // {"leapseconds":["+1972-06-30", "+1972-12-31", "+1973-12-31", "+1974-12-31", "+1975-12-31", "+1976-12-31", "+1977-12-31", "+1982-06-30", "+1983-06-30", "+1985-06-30", "+1987-12-31", "+1989-12-31", "+1990-12-31", "+1992-06-30", "+1993-06-30", "+1994-06-30", "+1995-12-31", "+1997-06-30", "+1998-12-31", "+2005-12-31", "+2008-12-31", "+2012-06-30", "+2015-06-30"]} char *leapseconds[] = { "+1972-06-30", "+1972-12-31", "+1973-12-31", "+1974-12-31", "+1975-12-31", "+1976-12-31", "+1977-12-31", "+1982-06-30", "+1983-06-30", "+1985-06-30", "+1987-12-31", "+1989-12-31", "+1990-12-31", "+1992-06-30", "+1993-06-30", "+1994-06-30", "+1995-12-31", "+1997-06-30", "+1998-12-31", "+2005-12-31", "+2008-12-31", "+2012-06-30", "+2015-06-30" }; struct tai leaptais[sizeof(leapseconds)/sizeof(*leapseconds)]; char *dayname[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" } ; static int32_t times365[4] = { 0, 365, 730, 1095 } ; static int32_t times36524[4] = { 0, 36524, 73048, 109572 } ; static int32_t montab[12] = { 0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337 } ; // month length after february is (306 * m + 5) / 10 int32_t taidate_mjd(struct taidate cd) { int32_t y,m,d; d = cd.day - 678882L; m = cd.month - 1; y = cd.year; d += 146097L * (y / 400); y %= 400; if (m >= 2) m -= 2; else { m += 10; --y; } y += (m / 12); m %= 12; if (m < 0) { m += 12; --y; } d += montab[m]; d += 146097L * (y / 400); y %= 400; if (y < 0) { y += 400; d -= 146097L; } d += times365[y & 3]; y >>= 2; d += 1461L * (y % 25); y /= 25; d += times36524[y & 3]; return d; } struct tai utc2tai(uint32_t timestamp) { struct tai t; memset(&t,0,sizeof(t)); t.x = (timestamp + TAI_UTC_DIFF); return(t); } uint32_t tai2utc(struct tai t) { t.x -= TAI_UTC_DIFF; return((uint32_t)t.x); } uint64_t tai2utime(struct tai t) { uint64_t mjd; uint64_t timestamp; struct taitime ct = tai2time(t,0,0); mjd = taidate_mjd(ct.date); timestamp = ((uint64_t)(mjd * 24*3600 + ct.hour*3600 + ct.minute*60 + ct.second)); return(timestamp); } struct tai tai_now() { struct tai t; uint64_t now = time(NULL); t.x = TAI_UTC_DIFF + now; t.millis = OS_milliseconds() - First_TAI.millis; if ( First_TAI.x == 0 ) { First_TAI = t, First_utc = (uint32_t)now; #ifndef DISABLE_LEAPS UTC_ADJUST = -36; #endif printf("TAINOW.%llu %03.3f UTC.%u vs %u [diff %d]\n",(long long)t.x,t.millis,First_utc,tai2utc(t),UTC_ADJUST); } return(t); } struct tai leapsecs_add(struct tai t,int32_t hit) { #ifndef DISABLE_LEAPS int32_t i; uint64_t u; u = t.x; if ( t.x > leaptais[(sizeof(leaptais)/sizeof(*leaptais)) - 1].x ) u += (sizeof(leaptais)/sizeof(*leaptais));//, printf("add leap.%ld\n",(sizeof(leaptais)/sizeof(*leaptais))); else { for (i=0; i leaptais[i].x ) u++; } } t.x = u; #endif return(t); } struct tai taitime2tai(struct taitime ct) { int32_t day,s; struct tai t; day = taidate_mjd(ct.date); s = ct.hour * 60 + ct.minute; s = (s - ct.offset) * 60 + ct.second; t.x = day * 86400ULL + 4611686014920671114ULL + (uint64_t)s; t.millis = ct.millis; return(leapsecs_add(t,ct.second == 60)); } double tai_diff(struct tai reftai,struct tai cmptai) { double diff; //char str[111],str2[111]; printf("start [%f %f] %s vs %s\n",reftai.millis,cmptai.millis,tai_str(str,reftai),tai_str(str2,cmptai)); reftai = taitime2tai(tai2time(reftai,0,0)); cmptai = taitime2tai(tai2time(cmptai,0,0)); diff = (cmptai.x - reftai.x) * 1000. + (cmptai.millis - reftai.millis); //printf("%s vs %s -> diff [%d] %f [%llu - %llu]\n\n",tai_str(str,reftai),tai_str(str2,cmptai),(int32_t)(cmptai.x - reftai.x),diff,(long long)cmptai.x,(long long)reftai.x); return(diff); } struct tai taidate_scan(char *s,int32_t numleaps) { int32_t z,c,sign = 1; char *t = s; struct taidate cd; struct tai st; st.x = 0; if (*t == '-') { ++t; sign = -1; } else if ( *t == '+' ) t++; z = 0; while ((c = (uint8_t) (*t - '0')) <= 9) { z = z * 10 + c; ++t; } cd.year = z * sign; if (*t++ != '-') return(st); z = 0; while ((c = (uint8_t) (*t - '0')) <= 9) { z = z * 10 + c; ++t; } cd.month = z; if (*t++ != '-') return(st); z = 0; while ((c = (uint8_t) (*t - '0')) <= 9) { z = z * 10 + c; ++t; } cd.day = z; //printf("year.%d month.%d day.%d numleaps.%d\n",cd.year,cd.month,cd.day,numleaps); st.x = (taidate_mjd(cd) + 1) * 86400ULL + 4611686014920671114ULL + numleaps; return(st); } int32_t leapsecs_sub(struct tai *lt) { #ifndef DISABLE_LEAPS char out[101],x[TAI_PACK]; double packerr; int32_t weekday,yearday,i,j,s; uint64_t u; struct tai t,t2; struct taitime ct2; if ( leaptais[0].x == 0 ) { for (i=0; ix; if ( u > leaptais[sizeof(leaptais)/sizeof(*leaptais)-1].x ) lt->x -= (sizeof(leaptais)/sizeof(*leaptais) - 1); else { s = 0; for (i=0; ix = u - s; return(1); } } lt->x = u - s; } #endif return(0); } char *tai_str(char *str,struct tai t) { struct taitime ct; ct = tai2time(t,0,0); sprintf(str,"%d-%02d-%02d %02d:%02d:%02d %03.3f",ct.date.year,ct.date.month,ct.date.day,ct.hour,ct.minute,ct.second,ct.millis); return(str); } /*char *utc_str(char *str,struct tai t) { t.x += UTC_ADJUST; return(tai_str(str,t)); }*/ char *utc_str(char *str,uint32_t utc) { struct taitime ct; struct tai t; t = utc2tai(utc); t.x += UTC_ADJUST; ct = tai2time(t,0,0); sprintf(str,"%d-%02d-%02dT%02d:%02d:%02dZ",ct.date.year,ct.date.month,ct.date.day,ct.hour,ct.minute,ct.second); return(str); } double OS_milliseconds() { return(OS_portable_milliseconds()); } int32_t calc_datenum(int32_t year,int32_t month,int32_t day) { return((year * 10000) + (month * 100) + day); } int32_t extract_datenum(int32_t *yearp,int32_t *monthp,int32_t *dayp,int32_t datenum) { *yearp = datenum / 10000, *monthp = (datenum / 100) % 100, *dayp = (datenum % 100); if ( *yearp >= 2000 && *yearp <= 2038 && *monthp >= 1 && *monthp <= 12 && *dayp >= 1 && *dayp <= 31 ) return(datenum); else return(-1); } uint32_t OS_conv_datenum(int32_t datenum,int32_t hour,int32_t minute,int32_t second) // datenum+H:M:S -> unix time { int32_t year,month,day; struct tai t; struct taitime ct; if ( 1 ) { if ( extract_datenum(&year,&month,&day,datenum) > 0 ) { ct = taitime_set(taidate_set(year,month,day),hour,minute,second); t = taitime2tai(ct); //char str[65]; printf("conv.(y%d m%d d%d %d:%d:%d) %s\n",year,month,day,hour,minute,second,tai_str(str,t)); return((uint32_t)tai2utc(t));//tai2utime(t)+788250398LL - 4294967296LL); } return(0); } else { #ifdef __PNACL return(0); #else struct tm t; memset(&t,0,sizeof(t)); t.tm_year = (datenum / 10000) - 1900, t.tm_mon = ((datenum / 100) % 100) - 1, t.tm_mday = (datenum % 100); t.tm_hour = hour, t.tm_min = minute, t.tm_sec = second; return((uint32_t)timegm(&t)); #endif } } int32_t OS_conv_unixtime(struct tai *tp,int32_t *secondsp,time_t timestamp) // gmtime -> datenum + number of seconds { struct tm tm,*ptr; int32_t datenum; uint32_t checktime; char buf[64]; struct tai t; struct taitime ct; if ( 1 ) { *tp = t = utc2tai((uint32_t)timestamp); ct = tai2time(t,0,0); *secondsp = (ct.hour*3600 + ct.minute*60 + ct.second); return(calc_datenum(ct.date.year,ct.date.month,ct.date.day)); } else { if ( (ptr= gmtime(×tamp)) != 0 ) tm = *ptr;; strftime(buf,sizeof(buf), "%Y-%m-%dT%H:%M:%SZ",&tm); //printf("%s\n",buf); datenum = conv_date(secondsp,buf); if ( (checktime= OS_conv_datenum(datenum,*secondsp/3600,(*secondsp%3600)/60,*secondsp%60)) != timestamp ) { printf("error: timestamp.%u -> (%d + %d) -> %u\n",(uint32_t)timestamp,datenum,*secondsp,checktime); return(-1); } return(datenum); } } int32_t conv_date(int32_t *secondsp,char *date) { char origdate[64],tmpdate[64]; int32_t year,month,day,hour,min,sec,len; strcpy(origdate,date), strcpy(tmpdate,date), tmpdate[8 + 2] = 0; year = atoi(tmpdate), month = atoi(tmpdate+5), day = atoi(tmpdate+8); *secondsp = 0; if ( (len= (int32_t)strlen(date)) <= 10 ) hour = min = sec = 0; if ( len >= 18 ) { tmpdate[11 + 2] = 0, tmpdate[14 + 2] = 0, tmpdate[17 + 2] = 0; hour = atoi(tmpdate+11), min = atoi(tmpdate + 14), sec = atoi(tmpdate+17); if ( hour >= 0 && hour < 24 && min >= 0 && min < 60 && sec >= 0 && sec < 60 ) *secondsp = (3600*hour + 60*min + sec); else printf("ERROR: seconds.%d %d %d %d, len.%d\n",*secondsp,hour,min,sec,len); } //printf("(%s) -> Y.%d M.%d D.%d %d:%d:%d\n",date,year,month,day,hour,min,sec); sprintf(origdate,"%d-%02d-%02d",year,month,day); //2015-07-25T22:34:31Z if ( strcmp(tmpdate,origdate) != 0 ) { printf("conv_date date conversion error (%s) -> (%s)\n",origdate,date); return(-1); } return((year * 10000) + (month * 100) + day); } uint32_t OS_conv_utime(char *utime) { int32_t datenum,seconds; datenum = conv_date(&seconds,utime); return((uint32_t)OS_conv_datenum(datenum,seconds/3600,(seconds%3600)/60,seconds%60)); } int32_t expand_datenum(char *date,int32_t datenum) { int32_t year,month,day; date[0] = 0; if ( extract_datenum(&year,&month,&day,datenum) != datenum) return(-1); sprintf(date,"%d-%02d-%02d",year,month,day); return(0); } int32_t ecb_decrdate(int32_t *yearp,int32_t *monthp,int32_t *dayp,char *date,int32_t datenum) { static int lastday[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; int32_t year,month,day; year = datenum / 10000, month = (datenum / 100) % 100, day = (datenum % 100); //printf("%d -> %d %d %d\n",datenum,year,month,day); if ( --day <= 0 ) { if ( --month <= 0 ) { if ( --year < 2000 ) { printf("reached epoch start\n"); return(-1); } month = 12; } day = lastday[month]; if ( month == 2 && (year % 4) == 0 ) day++; } sprintf(date,"%d-%02d-%02d",year,month,day); //printf("%d -> %d %d %d (%s)\n",datenum,year,month,day,date); *yearp = year, *monthp = month, *dayp = day; return((year * 10000) + (month * 100) + day); } #ifdef ENABLE_TEST #include int main(int argc, const char * argv[]) { int i; char str[111],str2[111],str3[111],str4[111]; struct taitime ct; struct tai t,start = tai_now(); for (i=0; i<100; i++) { sleep(1); t = tai_now(); taidate_str(str2,tai2date(t)); printf("(%s) time.%s date.%s %ld start.%ld %s %u %u\n",tai_str(str3,t),taitime_str(str,ct),str2,(long)tai2utime(t),(long)tai2utime(start),utime_str(str4,t),tai2utc(t),(uint32_t)time(NULL)); } // insert code here... { char str[65]; struct tai t; double startmillis; int32_t datenum,seconds; uint64_t i,checkval,timestamp,now = (uint32_t)time(NULL); startmillis = OS_milliseconds(); for (i=0; i<1000000; i++) { timestamp = now - (rand() % 100000000LL); // range -100000000LL to +500000000LL datenum = OS_conv_unixtime(&t,&seconds,timestamp); // gmtime -> datenum + number of seconds checkval = OS_conv_datenum(datenum,seconds/3600,(seconds/60)%60,seconds%60); // datenum+H:M:S -> unix time if ( checkval != timestamp ) printf("%s i.%lld timestamp.%-12llu -> (%d:%06d) -> checkval.%-12llu diff.[%lld]\n",tai_str(str,t),(long long)i,(long long)timestamp,datenum,seconds,(long long)checkval,(long long)(timestamp-checkval)); } printf("million tai compares in %.3f microseconds per encode/decode\n",1000. * (OS_milliseconds()-startmillis)/i); } /* struct tai t = tai_now(); double diff,lastdiff = 0.; for (i=0; i<1000000; i++) { diff = tai_diff(t,tai_now()); if ( diff < lastdiff ) printf("embargo.x %f %llu.%3.3f %llu.%3.3f\n",tai_diff(t,tai_now()),(long long)t.x,t.millis,(long long)tai_now().x,tai_now().millis); lastdiff = diff; //usleep(100000); } getchar(); */ return 0; } #endif