static int check_tm(struct TM *tm) { /* Don't forget leap seconds */ assert(tm->tm_sec >= 0); /* Allow for just one positive leap second, which is what the C99 standard says. */ /* Two leap seconds in the same minute are not allowed (the C90 range 0..61 was a defect). */ assert(tm->tm_sec <= 60); 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_year)][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(tm->tm_year)]); #ifdef HAS_TM_TM_GMTOFF assert(tm->tm_gmtoff >= -24 * 60 * 60); assert(tm->tm_gmtoff <= 24 * 60 * 60); #endif return 1; }
static int check_tm(struct TM *tm) { /* 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_year)][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(tm->tm_year)]); #ifdef HAS_TM_TM_GMTOFF assert(tm->tm_gmtoff >= -24 * 60 * 60); assert(tm->tm_gmtoff <= 24 * 60 * 60); #endif return 1; }
static Time64_T S_timegm64(struct TM *date) { int days = 0; Time64_T seconds = 0; Year year; 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; /* Avoid overflowing the days integer */ seconds = days; seconds = seconds * 60 * 60 * 24; seconds += date->tm_hour * 60 * 60; seconds += date->tm_min * 60; seconds += date->tm_sec; return(seconds); }
// isoweek_from_date - // Computes the ISO week number from the specified date. static void isoweek_from_date ( int y, int m, int d, int * iso_week, int * iso_year ) { int y_leap = IS_LEAP ( y ), y_previous_leap = IS_LEAP ( y - 1 ) ; int doy = day_of_year ( y, m, d ) + 1 ; int weekday, jan1_weekday ; //if ( y_leap && m > 2 ) // doy ++ ; jan1_weekday = day_of_week ( y, 1, 1, 1 ) ; weekday = day_of_week ( y, m, d, 1 ) ; if ( ! weekday ) weekday = 7 ; if ( ! jan1_weekday ) jan1_weekday = 7 ; /* Find if Y M D falls in year Y-1, week# 52 or 53 */ if ( doy <= ( 8 - jan1_weekday ) && jan1_weekday > 4 ) { * iso_year = y - 1 ; if ( jan1_weekday == 5 || ( y_previous_leap && jan1_weekday == 6 ) ) * iso_week = 53 ; else * iso_week = 52 ; } else * iso_year = y ; /* Find if Y M D falls in year Y+1, week# 1 */ if ( * iso_year == y ) { int i = ( y_leap ) ? 366 : 365 ; if ( ( i - ( doy - y_leap ) ) < 4 - weekday ) { ( * iso_year ) ++ ; * iso_week = 1 ; return ; } } /* Find if Y M D falls in year Y, week# 1 through 53 */ if ( * iso_year == y ) { int j = doy + ( 7 - weekday ) + ( jan1_weekday - 1 ) ; * iso_week = j / 7 ; if ( jan1_weekday ) ( * iso_week ) -- ; } }
/* timegm() is not in the C or POSIX spec, but it is such a useful extension I would be remiss in leaving it out. Also I need it for localtime64() */ Time64_T timegm64(const struct TM *date) { Time64_T days = 0; Time64_T seconds = 0; Year year; Year orig_year = (Year)date->tm_year; int cycles = 0; if( orig_year > 100 ) { cycles = (orig_year - 100) / 400; orig_year -= cycles * 400; days += (Time64_T)cycles * days_in_gregorian_cycle; } else if( orig_year < -300 ) { cycles = (orig_year - 100) / 400; orig_year -= cycles * 400; days += (Time64_T)cycles * days_in_gregorian_cycle; } TRACE3("# timegm/ cycles: %d, days: %lld, orig_year: %lld\n", cycles, days, orig_year); if( orig_year > 70 ) { year = 70; while( year < orig_year ) { days += length_of_year[IS_LEAP(year)]; year++; } } else if ( orig_year < 70 ) { year = 69; do { days -= length_of_year[IS_LEAP(year)]; year--; } while( year >= orig_year ); } days += julian_days_by_month[IS_LEAP(orig_year)][date->tm_mon]; days += date->tm_mday - 1; seconds = days * 60 * 60 * 24; seconds += date->tm_hour * 60 * 60; seconds += date->tm_min * 60; seconds += date->tm_sec; return(seconds); }
UINT32 SecondsElapsedFromBaseYear ( IN UINT16 BaseYear, IN UINT16 Year, IN UINT8 Month, IN UINT8 Day, IN UINT8 Hour, IN UINT8 Minute, IN UINT8 Second ) { UINTN Seconds; UINT32 LeapYear; INTN Index; Seconds = 0; for (Index = BaseYear; Index < Year; Index ++) { if (IS_LEAP(Index)) { Seconds += DAYS_PER_LYEAR * SECS_PER_DAY; } else { Seconds += DAYS_PER_NYEAR * SECS_PER_DAY; } } LeapYear = IS_LEAP(Year); for (Index = 0; Index < Month - 1; Index ++) { Seconds += MonthLengths[LeapYear][Index] * SECS_PER_DAY; } for (Index = 0; Index < Day - 1; Index ++) { Seconds += SECS_PER_DAY; } for (Index = 0; Index < Hour; Index ++) { Seconds += SECS_PER_HOUR; } for (Index = 0; Index < Minute; Index ++) { Seconds += SECS_PER_MIN; } return (UINT32) (Seconds + Second); }
static bool dayvalid(EFI_TIME *Time) { if (Time->Day < 1) return false; if (Time->Day > dayofmonth[Time->Month - 1]) return false; /* check month 2 */ if (Time->Month == 2 && (!IS_LEAP(Time->Year) && Time->Day > 28)) return false; return true; }
// day_of_week - // Returns the day of week. // Taken from PHP source code. static int day_of_week ( int year, int month, int day, int iso ) { int c1, y1, m1, dow ; c1 = century_value ( year / 100 ) ; y1 = positive_mod ( year, 100 ) ; m1 = IS_LEAP ( year ) ? leap_year_table [ month ] : nonleap_year_table [ month ] ; dow = positive_mod ( ( c1 + y1 + m1 + ( y1 / 4 ) + day ), 7) ; if ( iso ) { if ( ! dow ) dow = 7 ; } return ( dow ) ; }
static PyObject* _mktime_ymd(PyObject* self, PyObject* arg) { if (!PyUnicode_CheckExact(arg)) return NULL; Py_UNICODE *src = PyUnicode_AS_UNICODE(arg), *bPtr; size_t year = 0, month = 0, day = 0, timestamp = 0, multiplier, size = PyUnicode_GET_SIZE(arg); if (size >= 4) { ATOI(src, src + 4, year); } if (size >= 6) { ATOI(src + 4, src + 6, month); } if (size >= 8) { ATOI(src + 6, src + 8, day); } if (year < 1970 || year > 2038 || month > 13 || day > 32) { Py_RETURN_NONE; } timestamp = YEARS[year - 1970] + (IS_LEAP(year) ? MONTHS2[month] : MONTHS1[month]) + DAYS[day]; return PyInt_FromSize_t(timestamp); }
static PyObject* _mktime_tuple(PyObject* self, PyObject* arg) { if (!PyTuple_CheckExact(arg)) return NULL; PyTupleObject* tuple = (PyTupleObject*) arg; size_t year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0, size = tuple->ob_size, timestamp = 0; if (size > 0) year = PyInt_AsSsize_t(tuple->ob_item[0]); if (size > 1) month = PyInt_AsSsize_t(tuple->ob_item[1]); if (size > 2) day = PyInt_AsSsize_t(tuple->ob_item[2]); if (size > 3) hour = PyInt_AsSsize_t(tuple->ob_item[3]); if (size > 4) minute = PyInt_AsSsize_t(tuple->ob_item[4]); if (size > 5) second = PyInt_AsSsize_t(tuple->ob_item[5]); if (year < 1970 || year > 2038 || month > 13 || day > 32 || hour > 24 || minute > 60 || second > 60) { Py_RETURN_NONE; } timestamp = YEARS[year - 1970] + (IS_LEAP(year) ? MONTHS2[month] : MONTHS1[month]) + DAYS[day] + HOURS[hour] + MINUTES[minute] + second; return PyInt_FromSize_t(timestamp); }
static Time64_T seconds_between_years(Year left_year, Year right_year) { int increment = (left_year > right_year) ? 1 : -1; Time64_T seconds = 0; int cycles; if( left_year > 2400 ) { cycles = (left_year - 2400) / 400; left_year -= cycles * 400; seconds += cycles * seconds_in_gregorian_cycle; } else if( left_year < 1600 ) { cycles = (left_year - 1600) / 400; left_year += cycles * 400; seconds += cycles * seconds_in_gregorian_cycle; } while( left_year != right_year ) { seconds += length_of_year[IS_LEAP(right_year - 1900)] * 60 * 60 * 24; right_year += increment; } return seconds * increment; }
static void addonehour(EFI_TIME *time) { if (time->Hour != 23) { time->Hour += 1; return; } time->Hour = 0; if ((time->Day != dayofmonth[time->Month - 1]) && (!(time->Month == 2 && (!IS_LEAP(time->Year) && time->Day == 28)))) { time->Day += 1; return; } time->Day = 1; if (time->Month != 12) { time->Month += 1; return; } time->Month = 1; time->Year += 1; return; }
static int day_of_year ( int y, int m, int d ) { return ( ( ( IS_LEAP ( y ) ) ? leap_day_count [m] : nonleap_day_count [m] ) + d - 1 ) ; }
struct TM *localtime64_r (const Time64_T *time, struct TM *local_tm) { time_t safe_time; struct tm safe_date; struct TM gm_tm; Year orig_year; int month_diff; assert(local_tm != NULL); /* Use the system localtime() if time_t is small enough */ if( SHOULD_USE_SYSTEM_LOCALTIME(*time) ) { safe_time = *time; TRACE1("Using system localtime for %lld\n", *time); LOCALTIME_R(&safe_time, &safe_date); copy_tm_to_TM(&safe_date, local_tm); assert(check_tm(local_tm)); return local_tm; } if( gmtime64_r(time, &gm_tm) == NULL ) { TRACE1("gmtime64_r returned null for %lld\n", *time); return NULL; } orig_year = gm_tm.tm_year; if (gm_tm.tm_year > (2037 - 1900) || gm_tm.tm_year < (1970 - 1900) ) { TRACE1("Mapping tm_year %lld to safe_year\n", (Year)gm_tm.tm_year); gm_tm.tm_year = safe_year((Year)(gm_tm.tm_year + 1900)) - 1900; } safe_time = timegm64(&gm_tm); if( LOCALTIME_R(&safe_time, &safe_date) == NULL ) { TRACE1("localtime_r(%d) returned NULL\n", (int)safe_time); return NULL; } copy_tm_to_TM(&safe_date, local_tm); local_tm->tm_year = orig_year; if( local_tm->tm_year != orig_year ) { TRACE2("tm_year overflow: tm_year %lld, orig_year %lld\n", (Year)local_tm->tm_year, (Year)orig_year); #ifdef EOVERFLOW errno = EOVERFLOW; #endif return NULL; } month_diff = local_tm->tm_mon - gm_tm.tm_mon; /* When localtime is Dec 31st previous year and gmtime is Jan 1st next year. */ if( month_diff == 11 ) { local_tm->tm_year--; } /* When localtime is Jan 1st, next year and gmtime is Dec 31st, previous year. */ if( month_diff == -11 ) { local_tm->tm_year++; } /* GMT is Jan 1st, xx01 year, but localtime is still Dec 31st in a non-leap xx00. There is one point in the cycle we can't account for which the safe xx00 year is a leap year. So we need to correct for Dec 31st comming out as the 366th day of the year. */ if( !IS_LEAP(local_tm->tm_year) && local_tm->tm_yday == 365 ) local_tm->tm_yday--; assert(check_tm(local_tm)); return local_tm; }
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; int leap; Time64_T m; Time64_T time = *in_time; Year year = 70; int cycles = 0; assert(p != NULL); /* Use the system gmtime() if time_t is small enough */ if( SHOULD_USE_SYSTEM_GMTIME(*in_time) ) { time_t safe_time = *in_time; struct tm safe_date; GMTIME_R(&safe_time, &safe_date); copy_tm_to_TM(&safe_date, p); assert(check_tm(p)); return p; } #ifdef HAS_TM_TM_GMTOFF p->tm_gmtoff = 0; #endif p->tm_isdst = 0; #ifdef HAS_TM_TM_ZONE p->tm_zone = "UTC"; #endif v_tm_sec = (int)(time % 60); time /= 60; v_tm_min = (int)(time % 60); time /= 60; v_tm_hour = (int)(time % 24); time /= 24; v_tm_tday = time; WRAP (v_tm_sec, v_tm_min, 60); WRAP (v_tm_min, v_tm_hour, 60); WRAP (v_tm_hour, v_tm_tday, 24); v_tm_wday = (int)((v_tm_tday + 4) % 7); if (v_tm_wday < 0) v_tm_wday += 7; m = v_tm_tday; if (m >= CHEAT_DAYS) { year = CHEAT_YEARS; m -= CHEAT_DAYS; } if (m >= 0) { /* Gregorian cycles, this is huge optimization for distant times */ cycles = (int)(m / (Time64_T) days_in_gregorian_cycle); if( cycles ) { m -= (cycles * (Time64_T) days_in_gregorian_cycle); year += (cycles * years_in_gregorian_cycle); } /* Years */ leap = IS_LEAP (year); while (m >= (Time64_T) length_of_year[leap]) { m -= (Time64_T) length_of_year[leap]; year++; leap = IS_LEAP (year); } /* Months */ v_tm_mon = 0; while (m >= (Time64_T) days_in_month[leap][v_tm_mon]) { m -= (Time64_T) days_in_month[leap][v_tm_mon]; v_tm_mon++; } } else { year--; /* Gregorian cycles */ cycles = (int)((m / (Time64_T) days_in_gregorian_cycle) + 1); if( cycles ) { m -= (cycles * (Time64_T) days_in_gregorian_cycle); year += (cycles * years_in_gregorian_cycle); } /* Years */ leap = IS_LEAP (year); while (m < (Time64_T) -length_of_year[leap]) { m += (Time64_T) length_of_year[leap]; year--; leap = IS_LEAP (year); } /* Months */ v_tm_mon = 11; while (m < (Time64_T) -days_in_month[leap][v_tm_mon]) { m += (Time64_T) days_in_month[leap][v_tm_mon]; v_tm_mon--; } m += (Time64_T) days_in_month[leap][v_tm_mon]; } p->tm_year = year; if( p->tm_year != year ) { #ifdef EOVERFLOW errno = EOVERFLOW; #endif return NULL; } /* At this point m is less than a year so casting to an int is safe */ p->tm_mday = (int) m + 1; p->tm_yday = julian_days_by_month[leap][v_tm_mon] + (int)m; p->tm_sec = v_tm_sec; p->tm_min = v_tm_min; p->tm_hour = v_tm_hour; p->tm_mon = v_tm_mon; p->tm_wday = v_tm_wday; assert(check_tm(p)); return p; }
SEXP make_d(SEXP year, SEXP month, SEXP day) { if(!isInteger(year)) error("year must be integer"); if(!isInteger(month)) error("month must be integer"); if(!isInteger(day)) error("day must be integer"); R_len_t n = LENGTH(year); if(n != LENGTH(month)) error("length of 'month' vector is not the same as that of 'year'"); if(n != LENGTH(day)) error("length of 'day' vector is not the same as that of 'year'"); int* pyear = INTEGER(year); int* pmonth = INTEGER(month); int* pday = INTEGER(day); SEXP res = allocVector(REALSXP, n); double *data = REAL(res); for(int i = 0; i < n; i++) { // main accumulator double SECS = 0.0; int y = pyear[i]; int m = pmonth[i]; int d = pday[i]; if(y == NA_INTEGER || m == NA_INTEGER || d == NA_INTEGER) { data[i] = NA_REAL; } else { if ( 0 < m && m < 13 ) SECS += sm[m]; else { data[i] = NA_REAL; continue; } if ( 0 < d && d < 32 ) SECS += (d - 1) * 86400; else { data[i] = NA_REAL; continue; } int is_leap = IS_LEAP(y); if(check_ymd(y, m, d, is_leap)){ SECS += d30; y -= 2000; SECS += y * yearlen; SECS += adjust_leap_years(y, m, is_leap); data[i] = SECS; } else { data[i] = NA_REAL; } } } return res; }
SEXP make_dt(SEXP year, SEXP month, SEXP day, SEXP hour, SEXP minute, SEXP second) { if(!isInteger(year)) error("year must be integer"); if(!isInteger(month)) error("month must be integer"); if(!isInteger(day)) error("day must be integer"); if(!isInteger(hour)) error("hour must be integer"); if(!isInteger(minute)) error("minute must be integer"); if(!isNumeric(second)) error("second must be numeric"); R_len_t n = LENGTH(year); if(n != LENGTH(month)) error("length of 'month' vector is not the same as that of 'year'"); if(n != LENGTH(day)) error("length of 'day' vector is not the same as that of 'year'"); if(n != LENGTH(hour)) error("length of 'hour' vector is not the same as that of 'year'"); if(n != LENGTH(minute)) error("length of 'minute' vector is not the same as that of 'year'"); if(n != LENGTH(second)) error("length of 'second' vector is not the same as that of 'year'"); int* pyear = INTEGER(year); int* pmonth = INTEGER(month); int* pday = INTEGER(day); int* phour = INTEGER(hour); int* pminute = INTEGER(minute); int int_second = TYPEOF(second) == INTSXP; SEXP res = allocVector(REALSXP, n); double *data = REAL(res); for(int i = 0; i < n; i++) { // main accumulator double SECS = 0.0; int y = pyear[i]; int m = pmonth[i]; int d = pday[i]; int H = phour[i]; int M = pminute[i]; int naS; double S; if(int_second){ S = (double) INTEGER(second)[i]; naS = INTEGER(second)[i] == NA_INTEGER; } else { S = REAL(second)[i]; naS = ISNA(S); } if(naS || y == NA_INTEGER || m == NA_INTEGER || d == NA_INTEGER || H == NA_INTEGER || M == NA_INTEGER) { data[i] = NA_REAL; } else { if ( 0 < m && m < 13 ) SECS += sm[m]; else { data[i] = NA_REAL; continue; } if ( 0 < d && d < 32 ) SECS += (d - 1) * 86400; else { data[i] = NA_REAL; continue; } if( H < 25 ) SECS += H * 3600; else { data[i] = NA_REAL; continue; } if ( M < 61 ) SECS += M * 60; else{ data[i] = NA_REAL; continue; } // allow leap seconds if ( S < 62 ) { SECS += S; } else { data[i] = NA_REAL; continue; } int is_leap = IS_LEAP(y); if(check_ymd(y, m, d, is_leap)){ SECS += d30; y -= 2000; SECS += y * yearlen; SECS += adjust_leap_years(y, m, is_leap); data[i] = SECS; } else { data[i] = NA_REAL; } } } return res; }
/*============================================================================================================== * * NAME * wake_builtin_date - Formats a date. * * PROTOTYPE * $(<date> [ format [, time] ] ) * $(<gmdate> [ format [, time] ] ) * * DESCRIPTION * Formats a time value. * * PARAMETERS * format - * A format string, that accepts the same specifiers than the PHP date() function. * If not specified, the default format is 'Y-m-d H:i:s'. * * time - * A time value. If not specified, the current Unix time is used. * *==============================================================================================================*/ static char * __wake_builtin_date ( char * output, char * format, time_t time_value, int is_localtime ) { struct tm * tm = ( is_localtime ) ? localtime ( & time_value ) : gmtime ( & time_value ) ; char * p = format ; int ap_hour ; char buffer [128] ; char * buffer_p ; int iso_year, iso_week ; int iso_values_set = 0, tz_set = 0 ; tm -> tm_year += 1900 ; ap_hour = ( tm -> tm_hour % 12 ) ? tm -> tm_hour % 12 : 12 ; while ( * p ) { buffer_p = NULL ; * buffer = 0 ; switch ( * p ) { // 'a' : lowercase "am" or "pm" case 'a' : buffer_p = ( tm -> tm_hour >= 12 ) ? "pm" : "am" ; break ; // 'A' : uppercase "AM" or "PM" case 'A' : buffer_p = ( tm -> tm_hour >= 12 ) ? "PM" : "AM" ; break ; // 'B' : internet Swatch hour case 'B' : { int result = ( ( ( time_value - ( time_value - ( ( time_value % 86400 ) + 3600 ) ) ) * 10 ) / 864 ) ; while ( result < 0 ) result += 1000 ; result %= 1000 ; sprintf ( buffer, "%03d", result ) ; break ; } // 'c' : Full date, in ISO8601 format case 'c' : sprintf ( buffer, "%04d-%02d-%02dT%02d:%02d:%02d%s", tm -> tm_year, tm -> tm_mon + 1, tm -> tm_mday, tm -> tm_hour, tm -> tm_min, tm -> tm_sec, gmtoffset ( is_localtime, tm -> tm_gmtoff, 1 ) ) ; break ; // 'd' : day of month, as a two-digits number case 'd' : sprintf ( buffer, "%02d", tm -> tm_mday ) ; break ; // 'D' : short day name case 'D' : buffer_p = short_day_names [ tm -> tm_wday ] ; break ; // 'e' : Timezone name case 'e' : if ( ! tz_set ) tzset ( ) ; buffer_p = tzname [0] ; break ; // 'F' : Long month name case 'F' : buffer_p = long_month_names [ tm -> tm_mon ] ; break ; // 'g' : Hour without leading zero (1..12) case 'g' : sprintf ( buffer, "%d", ap_hour ) ; break ; // 'G' : Hour without leading zero (0..23) case 'G' : sprintf ( buffer, "%d", tm -> tm_hour ) ; break ; // 'h' : Hour with leading zero (01..12) case 'h' : sprintf ( buffer, "%02d", ap_hour ) ; break ; // 'H' : Hour with leading zero (00..23) case 'H' : sprintf ( buffer, "%02d", tm -> tm_hour ) ; break ; // 'i' : Minute, with leading zero (00..59) case 'i' : sprintf ( buffer, "%02d", tm -> tm_min ) ; break ; // 'I' : 1 if daylight savings time is active, 0 otherwise case 'I' : sprintf ( buffer, "%d", tm -> tm_isdst ) ; break ; // 'j' : day of month, without the leading zero case 'j' : sprintf ( buffer, "%d", tm -> tm_mday ) ; break ; // 'l' : Long day name case 'l' : buffer_p = long_day_names [ tm -> tm_wday ] ; break ; // 'L' : 1 is the year is leap, 0 otherwise case 'L' : sprintf ( buffer, "%d", IS_LEAP ( tm -> tm_year ) ) ; break ; // 'M' : short month name case 'M' : buffer_p = short_month_names [ tm -> tm_mon ] ; break ; // 'm' : month of year, as a two-digits number case 'm' : sprintf ( buffer, "%02d", tm -> tm_mon + 1 ) ; break ; // 'n' : month of year, without the leading zero case 'n' : sprintf ( buffer, "%d", tm -> tm_mon + 1 ) ; break ; // 'N' : day of week, from 1 (monday) to 7 (sunday) case 'N' : sprintf ( buffer, "%d", ( tm -> tm_wday ) ? tm -> tm_wday : 7 ) ; break ; // 'o' : ISO8601 year case 'o' : if ( ! iso_values_set ) { isoweek_from_date ( tm -> tm_year, tm -> tm_mon, tm -> tm_mday, & iso_week, & iso_year ) ; iso_values_set = 1 ; } sprintf ( buffer, "%d", iso_year ) ; break ; // 'O' : GMT offset in ISO8601 format (+0200) case 'O' : buffer_p = gmtoffset ( is_localtime, tm -> tm_gmtoff, 0 ) ; break ; // 'P' : GMT offset in RFC822 format (+02:00) case 'P' : buffer_p = gmtoffset ( is_localtime, tm -> tm_gmtoff, 1 ) ; break ; // 'r' : RFC822 date case 'r' : sprintf ( buffer, "%3s %02d %3s %04d %02d:%02d:%02d %s", short_day_names [ tm -> tm_wday ], tm -> tm_mday, short_month_names [ tm -> tm_mon ], tm -> tm_year, tm -> tm_hour, tm -> tm_min, tm -> tm_sec, gmtoffset ( is_localtime, tm -> tm_gmtoff, 0 ) ) ; break ; // 's' : Seconds, with leading zero (00..59) case 's' : sprintf ( buffer, "%02d", tm -> tm_sec ) ; break ; // 'S' : 2-letters english suffix for a day of month case 'S' : buffer_p = english_suffix ( tm -> tm_mday ) ; break ; // 't' : month length case 't' : sprintf ( buffer, "%d", MONTH_LENGTH ( tm -> tm_year, tm -> tm_mon ) ) ; break ; // 'T' : Timezone abbreviation case 'T' : if ( ! tz_set ) tzset ( ) ; buffer_p = tzname [1] ; break ; // 'u' : microsecond time. Always return '000000' since we accept an integer value as time case 'u' : buffer_p = "000000" ; break ; // 'U' : Unix time case 'U' : sprintf ( buffer, "%d", ( int ) time_value ) ; break ; // 'w' : day of week, from 0 (sunday) to 6 (saturday) case 'w' : sprintf ( buffer, "%d", tm -> tm_wday ) ; break ; // 'W' : ISO8601 week number (were weeks start on monday) case 'W' : if ( ! iso_values_set ) { isoweek_from_date ( tm -> tm_year, tm -> tm_mon, tm -> tm_mday, & iso_week, & iso_year ) ; iso_values_set = 1 ; } sprintf ( buffer, "%d", iso_week ) ; break ; // 'y' : 2-digits year case 'y' : sprintf ( buffer, "%02d", ( tm -> tm_year % 100 ) ) ; break ; // 'Y' : 4-digits year case 'Y' : sprintf ( buffer, "%04d", tm -> tm_year ) ; break ; // 'z' : day of year, from 0 to 365 case 'z' : sprintf ( buffer, "%d", tm -> tm_yday ) ; break ; // 'Z' : GMT offset in seconds case 'Z' : sprintf ( buffer, "%d", ( int ) tm -> tm_gmtoff ) ; break ; // Escape character : copy the next one without interpretation unless this is the last one case '\\' : if ( * ( p + 1 ) ) p ++ ; /* Intentionally fall through the default case */ // Other characters : copy as is default : buffer [0] = * p ; buffer [1] = 0 ; break ; } if ( buffer_p == NULL ) buffer_p = buffer ; output = variable_buffer_output ( output, buffer_p, strlen ( buffer_p ) ) ; p ++ ; } return ( output ) ; }
SEXP parse_dt(SEXP str, SEXP ord, SEXP formats, SEXP lt) { // STR: character vector of date-times. // ORD: formats (as in strptime) or orders (as in parse_date_time) // FORMATS: TRUE if ord is a string of formats (as in strptime) // LT: TRUE - return POSIXlt type list, FALSE - return POSIXct seconds if ( !isString(str) ) error("Date-time must be a character vector"); if ( !isString(ord) || (LENGTH(ord) > 1)) error("Format argument must be a character vector of length 1"); R_len_t n = LENGTH(str); int is_fmt = *LOGICAL(formats); int out_lt = *LOGICAL(lt); SEXP oYEAR, oMONTH, oDAY, oHOUR, oMIN, oSEC; if(out_lt){ oYEAR = PROTECT(allocVector(INTSXP, n)); oMONTH = PROTECT(allocVector(INTSXP, n)); oDAY = PROTECT(allocVector(INTSXP, n)); oHOUR = PROTECT(allocVector(INTSXP, n)); oMIN = PROTECT(allocVector(INTSXP, n)); oSEC = PROTECT(allocVector(REALSXP, n)); } else { oSEC = PROTECT(allocVector(REALSXP, n)); } const char *O = CHAR(STRING_ELT(ord, 0)); for (int i = 0; i < n; i++) { const char *c = CHAR(STRING_ELT(str, i)); const char *o = O; double secs = 0.0; // only accumulator for POSIXct case int y = 0, q = 0, m = 0, d = 0, H = 0, M = 0 , S = 0; int succeed = 1, O_format = 0, pm = 0, am = 0; // control logical // read order/format character by character while( *o && succeed ) { if( is_fmt && (*o != '%')) { // with fmt: non formatting characters should match exactly if ( *c == *o ) { c++; o++; } else succeed = 0; } else { if ( is_fmt ){ o++; // skip % } else if ( *o != 'O' && *o != 'z' && *o != 'p' && *o != 'm' && *o != 'b' && *o != 'B') { // skip non-digits // O, z, p formats are treated specially below while (*c && !DIGIT(*c)) c++; } if ( *o == 'O' ) { // Special two letter orders/formats: // Ou (Z), Oz (-0800), OO (-08:00) and Oo (-08) O_format = 1; o++; } else { O_format = 0; } if (!(DIGIT(*c) || O_format || *o == 'z' || *o == 'p' || *o == 'm' || *o == 'b' || *o == 'B')) { succeed = 0; } else { /* Rprintf("c=%c o=%c\n", *c, *o); */ switch( *o ) { case 'Y': // year in yyyy format y = parse_int(&c, 4, TRUE); if (y < 0) succeed = 0; break; case 'y': // year in yy format y = parse_int(&c, 2, FALSE); if (y < 0) succeed = 0; else if (y <= 68) y += 2000; else y += 1900; break; case 'q': // quarter q = parse_int(&c, 2, FALSE); if (!(0 < q && q < 5)) succeed = 0; break; case 'm': // month (allowing all months formats - m, b and B) SKIP_NON_ALPHANUMS(c); m = parse_int(&c, 2, FALSE); if (m == -1) { // failed m = parse_alpha_month(&c); if (m == 0) { // failed SKIP_NON_DIGITS(c); m = parse_int(&c, 2, FALSE); } } if (!(0 < m && m < 13)) succeed = 0; break; case 'b': // alpha English months (both abbreviated and long versions) case 'B': /* SKIP_NON_ALPHANUMS(c); */ m = parse_alpha_month(&c); succeed = m; /* Rprintf("succ=%d c=%c\n", succeed, *c); */ break; case 'd': // day d = parse_int(&c, 2, FALSE); if (!(0 < d && d < 32)) succeed = 0; break; case 'H': // hour 24 H = parse_int(&c, 2, FALSE); if (H > 24) succeed = 0; break; case 'I': // hour 12 H = parse_int(&c, 2, FALSE); if (H > 12) succeed = 0; break; case 'M': // minute M = parse_int(&c, 2, FALSE); if (M > 59) succeed = 0; break; case 'S': // second if( O_format && !is_fmt ){ while (*c && !DIGIT(*c)) c++; if (!*c) { succeed = 0; break; } } S = parse_int(&c, 2, FALSE); if (S < 62){ // allow leap seconds secs += S; if (O_format){ // Parse milliseconds; both . and , as decimal separator are allowed if( *c == '.' || *c == ','){ double ms = 0.0, msfact = 0.1; c++; while (DIGIT(*c)) { ms = ms + (*c - '0')*msfact; msfact *= 0.1; c++; } secs += ms; } } } else succeed = 0; break; case 'p': // AM/PM Both standard 'p' and lubridate 'Op' format SKIP_NON_ALPHANUMS(c); if (O_format) { // with Op format, p is optional (for order parsimony reasons) if (!(*c == 'P' || *c == 'p' || *c == 'A' || *c == 'a')) break; } if (*c == 'P' || *c == 'p') { pm = 1; c++; } else if (*c == 'A' || *c == 'a'){ am = 1; c++; } else { succeed = 0; } if (succeed && !(*c && (*c == 'M' || *c == 'm'))){ succeed = 0; } if (succeed) c++; break; case 'u': // %Ou: "2013-04-16T04:59:59Z" if( O_format ) if( *c == 'Z' || *c == 'z') c++; else succeed = 0; else succeed = 0; break; case 'z': // for %z: "+O100" or "+O1" or "+01:00" if( !O_format ) { if( !is_fmt ) { while (*c && *c != '+' && *c != '-' && *c != 'Z') c++; // skip non + - if( !*c ) { succeed = 0; break; }; } int Z = 0, sig; if( *c == 'Z') {c++; break;} else if ( *c == '+' ) sig = -1; else if ( *c == '-') sig = 1; else {succeed = 0; break;} c++; Z = parse_int(&c, 2, FALSE); if (Z < 0) {succeed = 0; break;} secs += sig*Z*3600; if( *c == ':' ){ c++; if ( !DIGIT(*c) ) {succeed = 0; break;} } if( DIGIT(*c) ){ Z = 0; Z = parse_int(&c, 2, FALSE); secs += sig*Z*60; } break; } // else O_format %Oz: "+0100"; pass through case 'O': // %OO: "+01:00" case 'o': // %Oo: "+01" if( O_format ){ while (*c && *c != '+' && *c != '-' ) c++; // skip non + - int Z = 0, sig; if ( *c == '+' ) sig = -1; else if ( *c == '-') sig = 1; else { succeed = 0; break; } c++; Z = parse_int(&c, 2, FALSE); if (Z < 0) {succeed = 0; break;} secs += sig*Z*3600; if( *o == 'O'){ if ( *c == ':') c++; else { succeed = 0; break; } } if ( *o != 'o' ){ // z or O Z = parse_int(&c, 2, FALSE); if (Z < 0) {succeed = 0; break;} secs += sig*Z*60; } } else error("Unrecognized format '%c' supplied", *o); break; default: error("Unrecognized format %c supplied", *o); } o++; } } } // skip all remaining non digits if( !is_fmt ) while (*c && !DIGIT(*c)) c++; // If at least one subparser hasn't finished it's a failure. if ( *c || *o ) succeed = 0; int is_leap; // adjust months for quarter if (q > 1) m += (q - 1) * 3 + 1; if (succeed) { // leap year every 400 years; no leap every 100 years is_leap = IS_LEAP(y); // check month if (m == 2){ // no check for d > 0 because we allow missing days in parsing if (is_leap) succeed = d < 30; else succeed = d < 29; } else { succeed = d <= mdays[m]; } } // allow missing months and days if (m == 0) m = 1; if (d == 0) d = 1; if(pm){ if(H > 12) succeed = 0; else if (H < 12) H += 12; } if (am){ if (H > 12) succeed = 0; else if (H == 12) H = 0; } if (succeed) { if(out_lt){ INTEGER(oYEAR)[i] = y - 1900; INTEGER(oMONTH)[i] = m - 1; INTEGER(oDAY)[i] = d; INTEGER(oHOUR)[i] = H; INTEGER(oMIN)[i] = M; REAL(oSEC)[i] = secs; } else { secs += sm[m]; secs += (d - 1) * 86400; secs += H * 3600; secs += M * 60; // process leap years y -= 2000; secs += y * yearlen; secs += adjust_leap_years(y, m, is_leap); REAL(oSEC)[i] = secs + d30; } } else { if(out_lt){ INTEGER(oYEAR)[i] = NA_INTEGER; INTEGER(oMONTH)[i] = NA_INTEGER; INTEGER(oDAY)[i] = NA_INTEGER; INTEGER(oHOUR)[i] = NA_INTEGER; INTEGER(oMIN)[i] = NA_INTEGER; REAL(oSEC)[i] = NA_REAL; } else { REAL(oSEC)[i] = NA_REAL; } } } if (out_lt){ SEXP names, out; PROTECT(names = allocVector(STRSXP, 6)); for(int i = 0; i < 6; i++) SET_STRING_ELT(names, i, mkChar(ltnames[i])); PROTECT(out = allocVector(VECSXP, 6)); SET_VECTOR_ELT(out, 0, oSEC); SET_VECTOR_ELT(out, 1, oMIN); SET_VECTOR_ELT(out, 2, oHOUR); SET_VECTOR_ELT(out, 3, oDAY); SET_VECTOR_ELT(out, 4, oMONTH); SET_VECTOR_ELT(out, 5, oYEAR); setAttrib(out, R_NamesSymbol, names); UNPROTECT(8); return out; } else { UNPROTECT(1); return oSEC; } }