@@ -67,49 +67,62 @@ This file implements string parsing and creation for NumPy datetime.
67
67
* Returns 0 on success, -1 on failure.
68
68
*/
69
69
70
+ typedef enum {
71
+ COMPARISON_SUCCESS ,
72
+ COMPLETED_PARTIAL_MATCH ,
73
+ COMPARISON_ERROR
74
+ } DatetimePartParseResult ;
70
75
// This function will advance the pointer on format
71
76
// and decrement characters_remaining by n on success
72
- // On failure will return -1 without incrementing
73
- static int compare_format (const char * * format , int * characters_remaining ,
74
- const char * compare_to , int n , const int exact ) {
77
+ // On failure will return COMPARISON_ERROR without incrementing
78
+ // If `format_requirement` is PARTIAL_MATCH, and the `format` string has
79
+ // been exhausted, then return COMPLETED_PARTIAL_MATCH.
80
+ static DatetimePartParseResult compare_format (
81
+ const char * * format ,
82
+ int * characters_remaining ,
83
+ const char * compare_to ,
84
+ int n ,
85
+ const FormatRequirement format_requirement
86
+ ) {
87
+ if (format_requirement == INFER_FORMAT ) {
88
+ return COMPARISON_SUCCESS ;
89
+ }
90
+ if (* characters_remaining < 0 ) {
91
+ return COMPARISON_ERROR ;
92
+ }
93
+ if (format_requirement == PARTIAL_MATCH && * characters_remaining == 0 ) {
94
+ return COMPLETED_PARTIAL_MATCH ;
95
+ }
75
96
if (* characters_remaining < n ) {
76
- if (exact ) {
77
- // TODO(pandas-dev): in the future we should set a PyErr here
78
- // to be very clear about what went wrong
79
- return -1 ;
80
- } else if (* characters_remaining ) {
81
- // TODO(pandas-dev): same return value in this function as
82
- // above branch, but stub out a future where
83
- // we have a better error message
84
- return -1 ;
85
- } else {
86
- return 0 ;
87
- }
97
+ // TODO(pandas-dev): PyErr to differentiate what went wrong
98
+ return COMPARISON_ERROR ;
88
99
} else {
89
100
if (strncmp (* format , compare_to , n )) {
90
101
// TODO(pandas-dev): PyErr to differentiate what went wrong
91
- return -1 ;
102
+ return COMPARISON_ERROR ;
92
103
} else {
93
104
* format += n ;
94
105
* characters_remaining -= n ;
95
- return 0 ;
106
+ return COMPARISON_SUCCESS ;
96
107
}
97
108
}
98
- return 0 ;
109
+ return COMPARISON_SUCCESS ;
99
110
}
100
111
101
112
int parse_iso_8601_datetime (const char * str , int len , int want_exc ,
102
113
npy_datetimestruct * out ,
103
114
NPY_DATETIMEUNIT * out_bestunit ,
104
115
int * out_local , int * out_tzoffset ,
105
- const char * format , int format_len , int exact ) {
116
+ const char * format , int format_len ,
117
+ FormatRequirement format_requirement ) {
106
118
if (len < 0 || format_len < 0 )
107
119
goto parse_error ;
108
120
int year_leap = 0 ;
109
121
int i , numdigits ;
110
122
const char * substr ;
111
123
int sublen ;
112
124
NPY_DATETIMEUNIT bestunit = NPY_FR_GENERIC ;
125
+ DatetimePartParseResult comparison ;
113
126
114
127
/* If year-month-day are separated by a valid separator,
115
128
* months/days without leading zeroes will be parsed
@@ -139,8 +152,11 @@ int parse_iso_8601_datetime(const char *str, int len, int want_exc,
139
152
while (sublen > 0 && isspace (* substr )) {
140
153
++ substr ;
141
154
-- sublen ;
142
- if (compare_format (& format , & format_len , " " , 1 , exact )) {
155
+ comparison = compare_format (& format , & format_len , " " , 1 , format_requirement );
156
+ if (comparison == COMPARISON_ERROR ) {
143
157
goto parse_error ;
158
+ } else if (comparison == COMPLETED_PARTIAL_MATCH ) {
159
+ goto finish ;
144
160
}
145
161
}
146
162
@@ -155,8 +171,11 @@ int parse_iso_8601_datetime(const char *str, int len, int want_exc,
155
171
}
156
172
157
173
/* PARSE THE YEAR (4 digits) */
158
- if (compare_format (& format , & format_len , "%Y" , 2 , exact )) {
174
+ comparison = compare_format (& format , & format_len , "%Y" , 2 , format_requirement );
175
+ if (comparison == COMPARISON_ERROR ) {
159
176
goto parse_error ;
177
+ } else if (comparison == COMPLETED_PARTIAL_MATCH ) {
178
+ goto finish ;
160
179
}
161
180
162
181
out -> year = 0 ;
@@ -202,8 +221,12 @@ int parse_iso_8601_datetime(const char *str, int len, int want_exc,
202
221
++ substr ;
203
222
-- sublen ;
204
223
205
- if (compare_format (& format , & format_len , & ymd_sep , 1 , exact )) {
224
+ comparison = compare_format (& format , & format_len , & ymd_sep , 1 ,
225
+ format_requirement );
226
+ if (comparison == COMPARISON_ERROR ) {
206
227
goto parse_error ;
228
+ } else if (comparison == COMPLETED_PARTIAL_MATCH ) {
229
+ goto finish ;
207
230
}
208
231
/* Cannot have trailing separator */
209
232
if (sublen == 0 || !isdigit (* substr )) {
@@ -212,8 +235,11 @@ int parse_iso_8601_datetime(const char *str, int len, int want_exc,
212
235
}
213
236
214
237
/* PARSE THE MONTH */
215
- if (compare_format (& format , & format_len , "%m" , 2 , exact )) {
238
+ comparison = compare_format (& format , & format_len , "%m" , 2 , format_requirement );
239
+ if (comparison == COMPARISON_ERROR ) {
216
240
goto parse_error ;
241
+ } else if (comparison == COMPLETED_PARTIAL_MATCH ) {
242
+ goto finish ;
217
243
}
218
244
/* First digit required */
219
245
out -> month = (* substr - '0' );
@@ -258,14 +284,21 @@ int parse_iso_8601_datetime(const char *str, int len, int want_exc,
258
284
}
259
285
++ substr ;
260
286
-- sublen ;
261
- if (compare_format (& format , & format_len , & ymd_sep , 1 , exact )) {
287
+ comparison = compare_format (& format , & format_len , & ymd_sep , 1 ,
288
+ format_requirement );
289
+ if (comparison == COMPARISON_ERROR ) {
262
290
goto parse_error ;
291
+ } else if (comparison == COMPLETED_PARTIAL_MATCH ) {
292
+ goto finish ;
263
293
}
264
294
}
265
295
266
296
/* PARSE THE DAY */
267
- if (compare_format (& format , & format_len , "%d" , 2 , exact )) {
297
+ comparison = compare_format (& format , & format_len , "%d" , 2 , format_requirement );
298
+ if (comparison == COMPARISON_ERROR ) {
268
299
goto parse_error ;
300
+ } else if (comparison == COMPLETED_PARTIAL_MATCH ) {
301
+ goto finish ;
269
302
}
270
303
/* First digit required */
271
304
if (!isdigit (* substr )) {
@@ -306,15 +339,21 @@ int parse_iso_8601_datetime(const char *str, int len, int want_exc,
306
339
if ((* substr != 'T' && * substr != ' ' ) || sublen == 1 ) {
307
340
goto parse_error ;
308
341
}
309
- if (compare_format (& format , & format_len , substr , 1 , exact )) {
310
- goto parse_error ;
311
- }
342
+ comparison = compare_format (& format , & format_len , substr , 1 , format_requirement );
343
+ if (comparison == COMPARISON_ERROR ) {
344
+ goto parse_error ;
345
+ } else if (comparison == COMPLETED_PARTIAL_MATCH ) {
346
+ goto finish ;
347
+ }
312
348
++ substr ;
313
349
-- sublen ;
314
350
315
351
/* PARSE THE HOURS */
316
- if (compare_format (& format , & format_len , "%H" , 2 , exact )) {
352
+ comparison = compare_format (& format , & format_len , "%H" , 2 , format_requirement );
353
+ if (comparison == COMPARISON_ERROR ) {
317
354
goto parse_error ;
355
+ } else if (comparison == COMPLETED_PARTIAL_MATCH ) {
356
+ goto finish ;
318
357
}
319
358
/* First digit required */
320
359
if (!isdigit (* substr )) {
@@ -359,8 +398,11 @@ int parse_iso_8601_datetime(const char *str, int len, int want_exc,
359
398
if (sublen == 0 || !isdigit (* substr )) {
360
399
goto parse_error ;
361
400
}
362
- if (compare_format (& format , & format_len , ":" , 1 , exact )) {
401
+ comparison = compare_format (& format , & format_len , ":" , 1 , format_requirement );
402
+ if (comparison == COMPARISON_ERROR ) {
363
403
goto parse_error ;
404
+ } else if (comparison == COMPLETED_PARTIAL_MATCH ) {
405
+ goto finish ;
364
406
}
365
407
} else if (!isdigit (* substr )) {
366
408
if (!hour_was_2_digits ) {
@@ -370,8 +412,11 @@ int parse_iso_8601_datetime(const char *str, int len, int want_exc,
370
412
}
371
413
372
414
/* PARSE THE MINUTES */
373
- if (compare_format (& format , & format_len , "%M" , 2 , exact )) {
415
+ comparison = compare_format (& format , & format_len , "%M" , 2 , format_requirement );
416
+ if (comparison == COMPARISON_ERROR ) {
374
417
goto parse_error ;
418
+ } else if (comparison == COMPLETED_PARTIAL_MATCH ) {
419
+ goto finish ;
375
420
}
376
421
/* First digit required */
377
422
out -> min = (* substr - '0' );
@@ -405,8 +450,11 @@ int parse_iso_8601_datetime(const char *str, int len, int want_exc,
405
450
/* If we make it through this condition block, then the next
406
451
* character is a digit. */
407
452
if (has_hms_sep && * substr == ':' ) {
408
- if (compare_format (& format , & format_len , ":" , 1 , exact )) {
453
+ comparison = compare_format (& format , & format_len , ":" , 1 , format_requirement );
454
+ if (comparison == COMPARISON_ERROR ) {
409
455
goto parse_error ;
456
+ } else if (comparison == COMPLETED_PARTIAL_MATCH ) {
457
+ goto finish ;
410
458
}
411
459
++ substr ;
412
460
-- sublen ;
@@ -420,8 +468,11 @@ int parse_iso_8601_datetime(const char *str, int len, int want_exc,
420
468
}
421
469
422
470
/* PARSE THE SECONDS */
423
- if (compare_format (& format , & format_len , "%S" , 2 , exact )) {
471
+ comparison = compare_format (& format , & format_len , "%S" , 2 , format_requirement );
472
+ if (comparison == COMPARISON_ERROR ) {
424
473
goto parse_error ;
474
+ } else if (comparison == COMPLETED_PARTIAL_MATCH ) {
475
+ goto finish ;
425
476
}
426
477
/* First digit required */
427
478
out -> sec = (* substr - '0' );
@@ -448,17 +499,23 @@ int parse_iso_8601_datetime(const char *str, int len, int want_exc,
448
499
if (sublen > 0 && * substr == '.' ) {
449
500
++ substr ;
450
501
-- sublen ;
451
- if (compare_format (& format , & format_len , "." , 1 , exact )) {
502
+ comparison = compare_format (& format , & format_len , "." , 1 , format_requirement );
503
+ if (comparison == COMPARISON_ERROR ) {
452
504
goto parse_error ;
505
+ } else if (comparison == COMPLETED_PARTIAL_MATCH ) {
506
+ goto finish ;
453
507
}
454
508
} else {
455
509
bestunit = NPY_FR_s ;
456
510
goto parse_timezone ;
457
511
}
458
512
459
513
/* PARSE THE MICROSECONDS (0 to 6 digits) */
460
- if (compare_format (& format , & format_len , "%f" , 2 , exact )) {
514
+ comparison = compare_format (& format , & format_len , "%f" , 2 , format_requirement );
515
+ if (comparison == COMPARISON_ERROR ) {
461
516
goto parse_error ;
517
+ } else if (comparison == COMPLETED_PARTIAL_MATCH ) {
518
+ goto finish ;
462
519
}
463
520
numdigits = 0 ;
464
521
for (i = 0 ; i < 6 ; ++ i ) {
@@ -524,8 +581,11 @@ int parse_iso_8601_datetime(const char *str, int len, int want_exc,
524
581
while (sublen > 0 && isspace (* substr )) {
525
582
++ substr ;
526
583
-- sublen ;
527
- if (compare_format (& format , & format_len , " " , 1 , exact )) {
584
+ comparison = compare_format (& format , & format_len , " " , 1 , format_requirement );
585
+ if (comparison == COMPARISON_ERROR ) {
528
586
goto parse_error ;
587
+ } else if (comparison == COMPLETED_PARTIAL_MATCH ) {
588
+ goto finish ;
529
589
}
530
590
}
531
591
@@ -539,8 +599,11 @@ int parse_iso_8601_datetime(const char *str, int len, int want_exc,
539
599
540
600
/* UTC specifier */
541
601
if (* substr == 'Z' ) {
542
- if (compare_format (& format , & format_len , "%z" , 2 , exact )) {
602
+ comparison = compare_format (& format , & format_len , "%z" , 2 , format_requirement );
603
+ if (comparison == COMPARISON_ERROR ) {
543
604
goto parse_error ;
605
+ } else if (comparison == COMPLETED_PARTIAL_MATCH ) {
606
+ goto finish ;
544
607
}
545
608
/* "Z" should be equivalent to tz offset "+00:00" */
546
609
if (out_local != NULL ) {
@@ -561,8 +624,11 @@ int parse_iso_8601_datetime(const char *str, int len, int want_exc,
561
624
-- sublen ;
562
625
}
563
626
} else if (* substr == '-' || * substr == '+' ) {
564
- if (compare_format (& format , & format_len , "%z" , 2 , exact )) {
627
+ comparison = compare_format (& format , & format_len , "%z" , 2 , format_requirement );
628
+ if (comparison == COMPARISON_ERROR ) {
565
629
goto parse_error ;
630
+ } else if (comparison == COMPLETED_PARTIAL_MATCH ) {
631
+ goto finish ;
566
632
}
567
633
/* Time zone offset */
568
634
int offset_neg = 0 , offset_hour = 0 , offset_minute = 0 ;
@@ -647,8 +713,11 @@ int parse_iso_8601_datetime(const char *str, int len, int want_exc,
647
713
while (sublen > 0 && isspace (* substr )) {
648
714
++ substr ;
649
715
-- sublen ;
650
- if (compare_format (& format , & format_len , " " , 1 , exact )) {
716
+ comparison = compare_format (& format , & format_len , " " , 1 , format_requirement );
717
+ if (comparison == COMPARISON_ERROR ) {
651
718
goto parse_error ;
719
+ } else if (comparison == COMPLETED_PARTIAL_MATCH ) {
720
+ goto finish ;
652
721
}
653
722
}
654
723
0 commit comments