Skip to content

Commit

Permalink
[Fix #507] Period and duration parsers understand 0 units
Browse files Browse the repository at this point in the history
  • Loading branch information
vspinu committed Feb 7, 2017
1 parent f67a04c commit d515020
Show file tree
Hide file tree
Showing 5 changed files with 24 additions and 17 deletions.
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Version 1.6.0.9000

### BUG FIXES

* [#507](https://github.com/hadley/lubridate/issues/507) Period and duration parsers now understand 0 units.
* [#466](https://github.com/hadley/lubridate/pull/466) Fix wrong formats within ymd_h family of functions.
* [#472](https://github.com/hadley/lubridate/pull/472) Printing method for duration doesn't throw format error on fractional seconds.
* [#475](https://github.com/hadley/lubridate/pull/475) character<> comparisons is no longer slow.
Expand Down
2 changes: 1 addition & 1 deletion man/hms.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 6 additions & 5 deletions src/period.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,22 @@ static const char *en_units[] = {"SECS", "secs", "seconds",
#define N_PERIOD_UNITS 7

intUnit parse_period_unit (const char **c) {
// S=0, M=1, H=2, d=3, w=4, m=5, y=6
// why this macro doesn't work?
/* SKIP_NON_ALPHANUMS(*c); */
// units: invalid=-1, S=0, M=1, H=2, d=3, w=4, m=5, y=6
// SKIP_NON_ALPHANUMS(*c); // why this macro doesn't work here?
while(**c && !(ALPHA(**c) || DIGIT(**c))) (*c)++;;

intUnit out;
out.unit = -1;

out.val = parse_int(c, 100, FALSE);
if(out.val == 0) out.val = 1;

if(**c){
out.unit = parse_alphanum(c, en_units, N_EN_UNITS);
if (out.unit < 0){
return out;
} else {
// if only unit name supplied, default to 1 units
if(out.val == -1)
out.val = 1;
if(out.unit < 3) out.unit = 0;
else if (out.unit < 6) out.unit = 1;
else if (out.unit < 8) out.unit = 2;
Expand Down
23 changes: 14 additions & 9 deletions src/tparse.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,9 @@ SEXP parse_dt(SEXP str, SEXP ord, SEXP formats, SEXP lt) {
break;
case 'y': // year in yy format
y = parse_int(&c, 2, FALSE);
if (y <= 68)
if (y < 0)
succeed = 0;
else if (y <= 68)
y += 2000;
else
y += 1900;
Expand All @@ -132,9 +134,9 @@ SEXP parse_dt(SEXP str, SEXP ord, SEXP formats, SEXP lt) {
case 'm': // month (allowing all months formats - m, b and B)
SKIP_NON_ALPHANUMS(c);
m = parse_int(&c, 2, FALSE);
if (m == 0) {
if (m == -1) { // failed
m = parse_alpha_month(&c);
if (m == 0) {
if (m == 0) { // failed
SKIP_NON_DIGITS(c);
m = parse_int(&c, 2, FALSE);
}
Expand Down Expand Up @@ -217,7 +219,7 @@ SEXP parse_dt(SEXP str, SEXP ord, SEXP formats, SEXP lt) {
break;
case 'z':
// for %z: "+O100" or "+O1" or "+01:00"
if( !O_format ){
if( !O_format ) {
if( !is_fmt ) {
while (*c && *c != '+' && *c != '-' && *c != 'Z') c++; // skip non + -
if( !*c ) { succeed = 0; break; };
Expand All @@ -226,21 +228,23 @@ SEXP parse_dt(SEXP str, SEXP ord, SEXP formats, SEXP lt) {
if( *c == 'Z') {c++; break;}
else if ( *c == '+' ) sig = -1;
else if ( *c == '-') sig = 1;
else { succeed = 0; break; }
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) ) {succeed = 0; break;}
}
if( DIGIT(*c) ){
Z = 0;
Z = parse_int(&c, 2, FALSE);
secs += sig*Z*60;
}
break;
} // else %Oz: "+0100". Pass through.
}
// else O_format %Oz: "+0100"; pass through
case 'O':
// %OO: "+01:00"
case 'o':
Expand All @@ -253,14 +257,15 @@ SEXP parse_dt(SEXP str, SEXP ord, SEXP formats, SEXP lt) {
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 = 0;
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);
Expand Down
4 changes: 2 additions & 2 deletions src/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,14 @@ int check_ymd(int y, int m, int d, int is_leap){
/* parse N digit characters from **c. Return parsed non-negative integer. If
failed to pass N chars, return -1.*/
int parse_int (const char **c, const int N, const int strict) {
// maybe: fixme: this returns 0 if no parsing happened and strict = FALSE
int tN = N, X = 0;
while (DIGIT(**c) && tN > 0) {
X = X * 10 + (**c - '0');
(*c)++;
tN--;
}
if (strict && tN > 0) return -1;
if (strict && tN > 0) return -1; // not all numbers have been consumed
else if (tN == N) return -1; // no parsing happened
else return X;
}

Expand Down

0 comments on commit d515020

Please sign in to comment.