Coverage Report

Created: 2026-02-04 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tarantool/third_party/c-dt/dt_parse_iso.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2012-2015 Christian Hansen <chansen@cpan.org>
3
 * <https://github.com/chansen/c-dt>
4
 * All rights reserved.
5
 * 
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions are met: 
8
 * 
9
 * 1. Redistributions of source code must retain the above copyright notice, this
10
 *    list of conditions and the following disclaimer. 
11
 * 2. Redistributions in binary form must reproduce the above copyright notice,
12
 *    this list of conditions and the following disclaimer in the documentation
13
 *    and/or other materials provided with the distribution. 
14
 * 
15
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
 */
26
#include <stddef.h>
27
#include "dt_core.h"
28
#include "dt_valid.h"
29
30
static size_t
31
4.91k
count_digits(const unsigned char * const p, size_t i, const size_t len) {
32
4.91k
    const size_t n = i;
33
34
17.7M
    for(; i < len; i++) {
35
17.7M
        const unsigned char c = p[i] - '0';
36
17.7M
        if (c > 9)
37
4.50k
            break;
38
17.7M
    }
39
4.91k
    return i - n;
40
4.91k
}
41
42
static int
43
5.76k
parse_number(const unsigned char * const p, size_t i, const size_t len) {
44
5.76k
    int v = 0;
45
46
5.76k
    switch (len) {
47
91
        case 9: v += (p[i++] - '0') * 100000000;
48
97
        case 8: v += (p[i++] - '0') * 10000000;
49
317
        case 7: v += (p[i++] - '0') * 1000000;
50
365
        case 6: v += (p[i++] - '0') * 100000;
51
443
        case 5: v += (p[i++] - '0') * 10000;
52
1.30k
        case 4: v += (p[i++] - '0') * 1000;
53
3.06k
        case 3: v += (p[i++] - '0') * 100;
54
5.34k
        case 2: v += (p[i++] - '0') * 10;
55
5.76k
        case 1: v += (p[i++] - '0');
56
5.76k
    }
57
5.76k
    return v;
58
5.76k
}
59
60
static const int pow_10[10] = {
61
    1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000,
62
};
63
64
/*
65
 *  fffffffff
66
 */
67
68
static size_t
69
165
parse_fraction_digits(const unsigned char *p, size_t i, size_t len, int *fp) {
70
165
    size_t n, ndigits;
71
72
165
    ndigits = n = count_digits(p, i, len);
73
165
    if (ndigits < 1)
74
4
        return 0;
75
161
    if (ndigits > 9)
76
85
        ndigits = 9;
77
161
    if (fp)
78
161
        *fp = parse_number(p, i, ndigits) * pow_10[9 - ndigits];
79
161
    return n;
80
165
}
81
82
/*
83
 *  hh
84
 *  hhmm
85
 *  hhmmss
86
 *  hhmmss.fffffffff
87
 *  hhmmss,fffffffff
88
 */
89
90
size_t
91
956
dt_parse_iso_time_basic(const char *str, size_t len, int *sp, int *fp) {
92
956
    const unsigned char *p;
93
956
    int h, m, s, f;
94
956
    size_t n;
95
96
956
    p = (const unsigned char *)str;
97
956
    n = count_digits(p, 0, len);
98
956
    m = s = f = 0;
99
956
    switch (n) {
100
745
        case 2: /* hh */
101
745
            h = parse_number(p, 0, 2);
102
745
            goto hms;
103
52
        case 4: /* hhmm */
104
52
            h = parse_number(p, 0, 2);
105
52
            m = parse_number(p, 2, 2);
106
52
            goto hms;
107
140
        case 6: /* hhmmss */
108
140
            h = parse_number(p, 0, 2);
109
140
            m = parse_number(p, 2, 2);
110
140
            s = parse_number(p, 4, 2);
111
140
            break;
112
19
        default:
113
19
            return 0;
114
956
    }
115
116
    /* hhmmss.fffffffff */
117
140
    if (n < len && (p[n] == '.' || p[n] == ',')) {
118
87
        size_t r = parse_fraction_digits(p, ++n, len, &f);
119
87
        if (!r)
120
2
            return 0;
121
85
        n += r;
122
85
    }
123
124
935
  hms:
125
935
    if (h > 23 || m > 59 || s > 59) {
126
121
        if (!(h == 24 && m == 0 && s == 0 && f == 0))
127
82
            return 0;
128
121
    }
129
130
853
    if (sp)
131
853
        *sp = h * 3600 + m * 60 + s;
132
853
    if (fp)
133
853
        *fp = f;
134
853
    return n;
135
935
}
136
137
/*
138
 *  Z
139
 *  ±hh
140
 *  ±hhmm
141
 */
142
143
size_t
144
0
dt_parse_iso_zone_basic(const char *str, size_t len, int *op) {
145
0
    const unsigned char *p;
146
0
    int o, h, m, sign;
147
0
    size_t n;
148
149
0
    if (len < 1)
150
0
        return 0;
151
152
0
    p = (const unsigned char *)str;
153
0
    switch (*p) {
154
0
        case 'Z':
155
0
            o = 0;
156
0
            n = 1;
157
0
            goto zulu;
158
0
        case '+':
159
0
            sign = 1;
160
0
            break;
161
0
        case '-':
162
0
            sign = -1;
163
0
            break;
164
0
        default:
165
0
            return 0;
166
0
    }
167
168
0
    if (len < 3)
169
0
        return 0;
170
171
0
    n = count_digits(p, 1, len);
172
0
    m = 0;
173
0
    switch (n) {
174
0
        case 2: /* ±hh */
175
0
            h = parse_number(p, 1, 2);
176
0
            n = 3;
177
0
            break;
178
0
        case 4: /* ±hhmm */
179
0
            h = parse_number(p, 1, 2);
180
0
            m = parse_number(p, 3, 2);
181
0
            n = 5;
182
0
            break;
183
0
        default:
184
0
            return 0;
185
0
    }
186
187
0
    if (h > 23 || m > 59)
188
0
        return 0;
189
0
    o = sign * (h * 60 + m);
190
#ifdef DT_PARSE_ISO_STRICT
191
    if (o == 0 && sign < 0)
192
        return 0;
193
#endif
194
195
0
 zulu:
196
0
    if (op)
197
0
        *op = o;
198
0
    return n;
199
0
}
200
201
/*
202
 *  hh
203
 *  hh:mm
204
 *  hh:mm:ss
205
 *  hh:mm:ss.fffffffff
206
 *  hh:mm:ss,fffffffff
207
 */
208
209
size_t
210
171
dt_parse_iso_time_extended(const char *str, size_t len, int *sp, int *fp) {
211
171
    const unsigned char *p;
212
171
    int h, m, s, f;
213
171
    size_t n;
214
215
171
    p = (const unsigned char *)str;
216
171
    if (count_digits(p, 0, len) != 2)
217
2
        return 0;
218
219
169
    h = parse_number(p, 0, 2);
220
169
    m = s = f = 0;
221
169
    n = 2;
222
    
223
169
    if (len < 3 || p[2] != ':')
224
0
        goto hms;
225
226
169
    if (count_digits(p, 3, len) != 2)
227
7
        return 0;
228
229
162
    m = parse_number(p, 3, 2);
230
162
    n = 5;
231
232
162
    if (len < 6 || p[5] != ':')
233
33
        goto hms;
234
235
129
    if (count_digits(p, 6, len) != 2)
236
22
        return 0;
237
238
107
    s = parse_number(p, 6, 2);
239
107
    n = 8;
240
241
    /* hh:mm:ss.fffffffff */
242
107
    if (n < len && (p[n] == '.' || p[n] == ',')) {
243
78
        size_t r = parse_fraction_digits(p, ++n, len, &f);
244
78
        if (!r)
245
2
            return 0;
246
76
        n += r;
247
76
    }
248
249
138
  hms:
250
138
    if (h > 23 || m > 59 || s > 59) {
251
85
        if (!(h == 24 && m == 0 && s == 0 && f == 0))
252
54
            return 0;
253
85
    }
254
255
84
    if (sp)
256
84
        *sp = h * 3600 + m * 60 + s;
257
84
    if (fp)
258
84
        *fp = f;
259
84
    return n;
260
138
}
261
262
/*
263
 *  Z
264
 *  ±hh
265
 *  ±hh:mm
266
 */
267
268
size_t
269
0
dt_parse_iso_zone_extended(const char *str, size_t len, int *op) {
270
0
    const unsigned char *p;
271
0
    int o, h, m, sign;
272
0
    size_t n;
273
274
0
    if (len < 1)
275
0
        return 0;
276
277
0
    p = (const unsigned char *)str;
278
0
    switch (*p) {
279
0
        case 'Z':
280
0
            o = 0;
281
0
            n = 1;
282
0
            goto zulu;
283
0
        case '+':
284
0
            sign = 1;
285
0
            break;
286
0
        case '-':
287
0
            sign = -1;
288
0
            break;
289
0
        default:
290
0
            return 0;
291
0
    }
292
293
0
    if (len < 3 || count_digits(p, 1, len) != 2)
294
0
        return 0;
295
296
0
    h = parse_number(p, 1, 2);
297
0
    m = 0;
298
0
    n = 3;
299
300
0
    if (len < 4 || p[3] != ':')
301
0
        goto hm;
302
303
0
    if (count_digits(p, 4, len) != 2)
304
0
        return 0;
305
306
0
    m = parse_number(p, 4, 2);
307
0
    n = 6;
308
309
0
 hm:
310
0
    if (h > 23 || m > 59)
311
0
        return 0;
312
0
    o = sign * (h * 60 + m);
313
#ifdef DT_PARSE_ISO_STRICT
314
    if (o == 0 && sign < 0)
315
        return 0;
316
#endif
317
318
0
 zulu:
319
0
    if (op)
320
0
        *op = o;
321
0
    return n;
322
0
}
323
324
/*
325
 *  z
326
 *  Z
327
 *  GMT
328
 *  GMT±h
329
 *  GMT±hhmm
330
 *  GMT±h:mm
331
 *  GMT±hh:mm
332
 *  UTC
333
 *  UTC±h
334
 *  UTC±hhmm
335
 *  UTC±h:mm
336
 *  UTC±hh:mm
337
 *  ±h
338
 *  ±hh
339
 *  ±hhmm
340
 *  ±h:mm
341
 *  ±hh:mm
342
 */
343
344
size_t
345
216
dt_parse_iso_zone_lenient(const char *str, size_t len, int *op) {
346
216
    const unsigned char *p;
347
216
    int o, h, m, sign;
348
216
    size_t n;
349
350
216
    if (len < 1)
351
0
        return 0;
352
353
216
    p = (const unsigned char *)str;
354
216
    switch (*p) {
355
1
        case 'z':
356
2
        case 'Z':
357
2
            o = 0;
358
2
            n = 1;
359
2
            goto zulu;
360
22
        case 'G':
361
22
            if (len < 3 || p[1] != 'M' || p[2] != 'T')
362
22
                return 0;
363
0
            if (len > 3 && (p[3] == '+' || p[3] == '-')) {
364
0
                if (!(n = dt_parse_iso_zone_lenient(str + 3, len - 3, op)))
365
0
                    return 0;
366
0
                return n + 3;
367
0
            }
368
0
            o = 0;
369
0
            n = 3;
370
0
            goto zulu;
371
8
        case 'U':
372
8
            if (len < 3 || p[1] != 'T' || p[2] != 'C')
373
8
                return 0;
374
0
            if (len > 3 && (p[3] == '+' || p[3] == '-')) {
375
0
                if (!(n = dt_parse_iso_zone_lenient(str + 3, len - 3, op)))
376
0
                    return 0;
377
0
                return n + 3;
378
0
            }
379
0
            o = 0;
380
0
            n = 3;
381
0
            goto zulu;
382
11
        case '+':
383
11
            sign = 1;
384
11
            break;
385
114
        case '-':
386
114
            sign = -1;
387
114
            break;
388
59
        default:
389
59
            return 0;
390
216
    }
391
392
125
    if (len < 2)
393
3
        return 0;
394
395
122
    n = count_digits(p, 1, len);
396
122
    m = 0;
397
122
    switch (n) {
398
52
        case 1: /* ±h */
399
52
            h = parse_number(p, 1, 1);
400
52
            n = 2;
401
52
            break;
402
49
        case 2: /* ±hh */
403
49
            h = parse_number(p, 1, 2);
404
49
            n = 3;
405
49
            break;
406
8
        case 4: /* ±hhmm */
407
8
            h = parse_number(p, 1, 2);
408
8
            m = parse_number(p, 3, 2);
409
8
            n = 5;
410
8
            goto hm;
411
13
        default:
412
13
            return 0;
413
122
    }
414
    
415
101
    if (len < n + 1 || p[n] != ':')
416
54
        goto hm;
417
418
47
    if (count_digits(p, ++n, len) != 2)
419
13
        return 0;
420
421
34
    m = parse_number(p, n, 2);
422
34
    n += 2;
423
424
96
 hm:
425
96
    if (h > 23 || m > 59)
426
10
        return 0;
427
86
    o = sign * (h * 60 + m);
428
429
88
 zulu:
430
88
    if (op)
431
88
        *op = o;
432
88
    return n;
433
86
}
434
435
#ifdef DT_PARSE_ISO_TNT
436
/*
437
 * Count number of delimiting dashes, Ws or Qs in date string like
438
 *  5879611-07-11, or 2012-Q4-85, or 10000W521
439
 * Allows both ISO8601 and extended Tarantool datetime formats
440
 */
441
static size_t
442
1.91k
count_delims(const unsigned char *p, size_t i, size_t len) {
443
1.91k
    size_t n = 0;
444
445
14.7M
    for (; i < len; i++) {
446
14.7M
        switch (p[i]) {
447
11.4M
        case '0': case '1': case '2': case '3': case '4':
448
11.4M
        case '5': case '6': case '7': case '8': case '9':
449
11.4M
            continue;
450
76.7k
        case 'Q': case 'W':
451
3.25M
        case '-':
452
3.25M
            n++;
453
3.25M
            continue;
454
14.7M
        }
455
1.26k
        break;
456
14.7M
    }
457
1.91k
    return n;
458
1.91k
}
459
#endif
460
/*
461
 *  Basic      Extended
462
 *  20121224   2012-12-24   Calendar date   (ISO 8601)
463
 *  2012359    2012-359     Ordinal date    (ISO 8601)
464
 *  2012W521   2012-W52-1   Week date       (ISO 8601)
465
 *  2012Q485   2012-Q4-85   Quarter date
466
 *
467
 *  Tarantool extended ranges
468
 * #ifdef DT_PARSE_ISO_TNT
469
 *  -001-12-31      0000-01-01
470
 *  -5879610-06-22  5879611-07-11
471
 * #endif
472
 */
473
size_t
474
1.91k
dt_parse_iso_date(const char *str, size_t len, dt_t *dtp) {
475
1.91k
    const unsigned char *p = (const unsigned char *)str;
476
1.91k
    int y, x, d;
477
1.91k
    size_t n;
478
1.91k
    dt_t dt;
479
1.91k
    int head_n;
480
1.91k
#ifdef DT_PARSE_ISO_TNT
481
1.91k
    int sign = +1;
482
1.91k
    int dashes_n;
483
484
1.91k
    if (p[0] == '-') {
485
199
        sign = -1;
486
199
        p++;
487
199
        len--;
488
199
    }
489
1.91k
    dashes_n = count_delims(p, 0, len);
490
1.91k
#endif
491
492
1.91k
    head_n = n = count_digits(p, 0, len);
493
1.91k
    switch (n) {
494
0
#ifdef DT_PARSE_ISO_TNT
495
638
        case 3: /* -001-01-01 | 100W521 (extended Tarantool range) */
496
638
            if (!dashes_n)
497
10
                return 0;
498
628
            y = parse_number(p, 0, 3);
499
628
            break;
500
30
        case 4: /* -2001 (extended Tarantool range) | 2001-01-01 | 1000W521 */
501
30
            y = parse_number(p, 0, 4);
502
30
            break;
503
73
        case 5: /* 10000-01-01 | 10000W521 (extended Tarantool range) */
504
116
        case 6: /* 109000-01-01 | 109000W521 (extended Tarantool range) */
505
116
            if (!dashes_n)
506
2
                return 0;
507
114
            y = parse_number(p, 0, n);
508
114
            break;
509
989
        case 7: /* 5879611-07-11 | 1000000W521 (extended Tarantool range) */
510
989
            if (dashes_n > 0) {
511
213
                y = parse_number(p, 0, 7);
512
213
                break;
513
213
            }
514
            /* 2012359 (basic ordinal date) */
515
776
            y = parse_number(p, 0, 4);
516
776
            d = parse_number(p, 4, 3);
517
776
            p += 7;
518
776
            goto yd;
519
#else
520
        case 4: /* 2012 (year) */
521
            y = parse_number(p, 0, 4);
522
            break;
523
        case 7: /* 2012359 (basic ordinal date) */
524
            y = parse_number(p, 0, 4);
525
            d = parse_number(p, 4, 3);
526
            p += 7;
527
            goto yd;
528
#endif
529
45
        case 8: /* 20121224 (basic calendar date) */
530
45
            y = parse_number(p, 0, 4);
531
45
            x = parse_number(p, 4, 2);
532
45
            d = parse_number(p, 6, 2);
533
45
            p += 8;
534
45
            goto ymd;
535
99
        default:
536
99
            return 0;
537
1.91k
    }
538
539
985
    if (len < (n + 4))
540
18
        return 0;
541
542
967
    p += n;
543
967
    n = count_digits(p, 1, len);
544
967
    switch (p[0]) {
545
582
        case '-': /* 2012-359 | 2012-12-24 | 2012-W52-1 | 2012-Q4-85 */
546
582
            break;
547
0
#ifndef DT_PARSE_ISO_STRICT
548
182
        case 'Q': /* 2012Q485 */
549
182
            if (n != 3)
550
20
                return 0;
551
162
            x = parse_number(p, 1, 1);
552
162
            d = parse_number(p, 2, 2);
553
162
            p += 4;
554
162
            goto yqd;
555
0
#endif
556
202
        case 'W': /* 2012W521 */
557
202
            if (n != 3)
558
8
                return 0;
559
194
            x = parse_number(p, 1, 2);
560
194
            d = parse_number(p, 3, 1);
561
194
            p += 4;
562
194
            goto ywd;
563
1
        default:
564
1
            return 0;
565
967
    }
566
567
582
    switch (n) {
568
199
        case 0: /* 2012-Q4-85 | 2012-W52-1 */
569
199
            break;
570
31
        case 2: /* 2012-12-24 */
571
31
            if (p[3] != '-' || count_digits(p, 4, len) != 2)
572
30
                return 0;
573
1
            x = parse_number(p, 1, 2);
574
1
            d = parse_number(p, 4, 2);
575
1
            p += 6;
576
1
            goto ymd;
577
350
        case 3: /* 2012-359 */
578
350
            d = parse_number(p, 1, 3);
579
350
            p += 4;
580
350
            goto yd;
581
2
        default:
582
2
            return 0;
583
582
    }
584
585
199
    if (len < (head_n + 6))
586
3
        return 0;
587
588
196
    n = count_digits(p, 2, len);
589
196
    switch (p[1]) {
590
0
#ifndef DT_PARSE_ISO_STRICT
591
52
        case 'Q': /* 2012-Q4-85 */
592
52
            if (n != 1 || p[3] != '-' || count_digits(p, 4, len) != 2)
593
51
                return 0;
594
1
            x = parse_number(p, 2, 1);
595
1
            d = parse_number(p, 4, 2);
596
1
            p += 6;
597
1
            goto yqd;
598
0
#endif
599
65
        case 'W': /* 2012-W52-1 */
600
65
            if (n != 2 || p[4] != '-' || count_digits(p, 5, len) != 1)
601
62
                return 0;
602
3
            x = parse_number(p, 2, 2);
603
3
            d = parse_number(p, 5, 1);
604
3
            p += 6;;
605
3
            goto ywd;
606
79
        default:
607
79
            return 0;
608
196
    }
609
610
1.12k
  yd:
611
1.12k
#ifdef DT_PARSE_ISO_TNT
612
1.12k
    if (!dt_from_yd_checked(sign * y, d, &dt))
613
29
        return 0;
614
#else
615
    if (!dt_valid_yd(y, d))
616
        return 0;
617
    dt = dt_from_yd(y, d);
618
#endif
619
1.09k
    goto finish;
620
621
1.09k
  ymd:
622
46
#ifdef DT_PARSE_ISO_TNT
623
46
    if (!dt_from_ymd_checked(sign * y, x, d, &dt))
624
23
        return 0;;
625
#else
626
    if (!dt_valid_ymd(y, x, d))
627
        return 0;
628
    dt = dt_from_ymd(y, x, d);
629
#endif
630
23
    goto finish;
631
632
0
#ifndef DT_PARSE_ISO_STRICT
633
163
  yqd:
634
163
#ifdef DT_PARSE_ISO_TNT
635
163
    if (!dt_from_yqd_checked(sign * y, x, d, &dt))
636
28
        return 0;
637
#else
638
    if (!dt_valid_yqd(y, x, d))
639
        return 0;
640
    dt = dt_from_yqd(y, x, d);
641
#endif
642
135
    goto finish;
643
135
#endif
644
645
197
  ywd:
646
197
#ifdef DT_PARSE_ISO_TNT
647
197
    if (!dt_from_ywd_checked(sign * y, x, d, &dt))
648
45
        return 0;
649
#else
650
    if (!dt_valid_ywd(y, x, d))
651
        return 0;
652
    dt = dt_from_ywd(y, x, d);
653
#endif
654
655
1.40k
  finish:
656
#ifndef DT_PARSE_ISO_YEAR0
657
    if (y < 1)
658
        return 0;
659
#endif
660
1.40k
    if (dtp)
661
1.40k
        *dtp = dt;
662
1.40k
    return (p - (const unsigned char *)str);
663
197
}
664
665
/*
666
 *  Basic               Extended
667
 *  T12                 N/A
668
 *  T1230               T12:30
669
 *  T123045             T12:30:45
670
 *  T123045.123456789   T12:30:45.123456789
671
 *  T123045,123456789   T12:30:45,123456789
672
 *
673
 *  The time designator [T] may be omitted.
674
 */
675
676
size_t
677
1.12k
dt_parse_iso_time(const char *str, size_t len, int *sod, int *nsec) {
678
1.12k
    size_t n, r;
679
680
1.12k
    if (len < 2)
681
1
        return 0;
682
683
1.12k
    if (str[0] == 'T')
684
4
        r = 1, ++str, --len;
685
1.12k
    else
686
1.12k
        r = 0;
687
688
1.12k
    if (len < 2 || str[2] == ':')
689
171
        n = dt_parse_iso_time_extended(str, len, sod, nsec);
690
956
    else
691
956
        n = dt_parse_iso_time_basic(str, len, sod, nsec);
692
693
1.12k
    if (!n)
694
190
        return 0;
695
937
    return r + n;
696
1.12k
}
697
698
/* 
699
 *  Basic    Extended
700
 *  Z        N/A
701
 *  ±hh      N/A
702
 *  ±hhmm    ±hh:mm
703
 */
704
705
size_t
706
0
dt_parse_iso_zone(const char *str, size_t len, int *offset) {
707
0
    if (len < 3 || str[3] == ':')
708
0
        return dt_parse_iso_zone_extended(str, len, offset);
709
0
    else
710
0
        return dt_parse_iso_zone_basic(str, len, offset);
711
0
}
712