File Coverage

XS.xs
Criterion Covered Total %
statement 387 412 93.9
branch 262 316 82.9
condition n/a
subroutine n/a
pod n/a
total 649 728 89.1


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT
2             #include "EXTERN.h"
3             #include "perl.h"
4             #include "XSUB.h"
5              
6             #include "tstr_param.h"
7             #include "tstr_format.h"
8             #include "tstr_datetime.h"
9             #include "tstr_time2str.h"
10             #include "tstr_parsed.h"
11             #include "tstr_token_parse.h"
12             #include "tstr_calendar.h"
13             #include "tstr_time.h"
14             #include "tstr_regexp.h"
15             #include "tstr_parse.h"
16             #include "tstr_sv.h"
17             #include "tstr_carp.h"
18              
19             #if NVSIZE > 8
20             # define DEFAULT_PRECISION 9
21             #else
22             # define DEFAULT_PRECISION 6
23             #endif
24              
25             #define DEFAULT_PIVOT_YEAR 1950
26             #define NANOS_PER_SECOND TSTR_NANOS_PER_SECOND
27             #define MIN_EPOCH INT64_C(-62135596800)
28             #define MAX_EPOCH INT64_C(253402300799)
29             #define EPOCH_20500101 INT64_C(2524608000)
30              
31             #define MY_CXT_KEY "Time::Str::_cxt" XS_VERSION
32              
33             typedef struct {
34             REGEXP *regexps[TSTR_FORMAT_TYPE_COUNT];
35             tstr_sv_keys_t keys;
36             } my_cxt_t;
37              
38             START_MY_CXT
39              
40             #define SHARE_KEY(s) newSVpvn_share("" s "", sizeof(s) - 1, 0)
41              
42 28           static void init_keys(pTHX_ tstr_sv_keys_t *k) {
43 28           k->k_year = SHARE_KEY("year");
44 28           k->k_month = SHARE_KEY("month");
45 28           k->k_day = SHARE_KEY("day");
46 28           k->k_hour = SHARE_KEY("hour");
47 28           k->k_minute = SHARE_KEY("minute");
48 28           k->k_second = SHARE_KEY("second");
49 28           k->k_nanosecond = SHARE_KEY("nanosecond");
50 28           k->k_tz_offset = SHARE_KEY("tz_offset");
51 28           k->k_tz_utc = SHARE_KEY("tz_utc");
52 28           k->k_tz_abbrev = SHARE_KEY("tz_abbrev");
53 28           k->k_tz_annotation = SHARE_KEY("tz_annotation");
54 28           k->k_fraction = SHARE_KEY("fraction");
55 28           k->k_day_name = SHARE_KEY("day_name");
56 28           k->k_meridiem = SHARE_KEY("meridiem");
57 28           }
58              
59 28           static void load_regexps(pTHX_ my_cxt_t *cxt) {
60             HV *mapping;
61             HE *entry;
62             int i;
63              
64 28           dSP;
65              
66 28           ENTER;
67 28           SAVETMPS;
68              
69 28           load_module(PERL_LOADMOD_NOIMPORT, newSVpvs("Time::Str::Regexp"), NULL);
70              
71 28 50         PUSHMARK(SP);
72 28           call_pv("Time::Str::Regexp::mapping", G_SCALAR);
73 28           SPAGAIN;
74 28           mapping = (HV *)SvRV(POPs);
75 28           PUTBACK;
76              
77 672 100         for (i = 0; i < TSTR_FORMAT_TYPE_COUNT; i++)
78 644           cxt->regexps[i] = NULL;
79              
80 28           hv_iterinit(mapping);
81 644 100         while ((entry = hv_iternext(mapping))) {
82             STRLEN klen;
83 616 50         const char *key = HePV(entry, klen);
84 616           tstr_format_t fmt = tstr_format_from_string(key, klen);
85 616           SV *val = HeVAL(entry);
86             REGEXP *rx;
87              
88 616 50         if (fmt == TSTR_FORMAT_UNKNOWN)
89 0           croak("panic: Time::Str::Regexp::mapping() returned unknown format key '%.*s'",
90             (int)klen, key);
91              
92 616           rx = SvRX(val);
93 616 50         if (!rx)
94 0           croak("panic: Time::Str::Regexp::mapping() value for '%.*s' is not a regexp",
95             (int)klen, key);
96              
97 616           cxt->regexps[fmt] = ReREFCNT_inc(rx);
98             }
99              
100 28 50         FREETMPS;
101 28           LEAVE;
102 28           }
103              
104              
105             MODULE = Time::Str PACKAGE = Time::Str
106              
107             PROTOTYPES: DISABLE
108              
109             BOOT:
110             {
111             MY_CXT_INIT;
112 28           init_keys(aTHX_ &MY_CXT.keys);
113 28           load_regexps(aTHX_ &MY_CXT);
114             }
115              
116             #ifdef USE_ITHREADS
117              
118             void
119             CLONE(...)
120             CODE:
121             {
122             MY_CXT_CLONE;
123             init_keys(aTHX_ &MY_CXT.keys);
124             load_regexps(aTHX_ &MY_CXT);
125             PERL_UNUSED_VAR(items);
126             }
127              
128             #endif
129              
130             void
131             time2str(...)
132             PREINIT:
133 136 50         dXSTARG;
134             int64_t epoch;
135 136           int offset = 0;
136 136           int nanosecond = -1;
137 136           int precision = -1;
138 136           tstr_format_t fmt = TSTR_FORMAT_RFC3339;
139             tstr_datetime_t dt;
140             int i;
141             PPCODE:
142 136 100         if (items < 1 || !(items & 1))
    100          
143 2           croak("Usage: time2str(time [, format => 'RFC3339' ])");
144              
145 134           epoch = (int64_t)SvNV(ST(0));
146              
147 360 100         for (i = 1; i < items; i += 2) {
148             const char *key;
149             STRLEN klen;
150             SV *val;
151              
152 234           key = SvPV_const(ST(i), klen);
153 234           val = ST(i + 1);
154              
155 234           switch (tstr_param_from_string(key, klen)) {
156 123           case TSTR_PARAM_FORMAT:
157 123           fmt = tstr_sv_format(aTHX_ val);
158 122           break;
159 37           case TSTR_PARAM_OFFSET:
160 37           offset = tstr_sv_offset(aTHX_ val);
161 35           break;
162 47           case TSTR_PARAM_PRECISION:
163 47           precision = tstr_sv_precision(aTHX_ val);
164 45           break;
165 26           case TSTR_PARAM_NANOSECOND:
166 26           nanosecond = tstr_sv_nanosecond(aTHX_ val);
167 24           break;
168 1           default:
169 1           croak("Unrecognised named parameter: '%"SVf"'", ST(i));
170             }
171             }
172              
173 126 100         if (epoch < MIN_EPOCH || epoch > MAX_EPOCH)
    100          
174 2           croak("Parameter 'time' is out of range");
175              
176 124 100         if (nanosecond < 0 && SvNOK(ST(0))) {
    50          
177 100           NV t = SvNV(ST(0));
178 100           NV sec = Perl_floor(t);
179 100           NV fr = t - sec;
180 100 100         int scale_exp = (precision >= 0) ? precision : DEFAULT_PRECISION;
181 100           NV scale = Perl_pow(10.0, (NV)scale_exp);
182              
183 100           fr = Perl_floor(fr * scale + 0.5) / scale;
184 100           nanosecond = (int)Perl_floor(fr * NANOS_PER_SECOND + 0.5);
185 100           epoch = (int64_t)sec;
186              
187 100 100         if (nanosecond >= NANOS_PER_SECOND) {
188 4           nanosecond -= NANOS_PER_SECOND;
189 4           epoch++;
190             }
191             }
192              
193 124 50         if (nanosecond < 0)
194 0           nanosecond = 0;
195              
196 124 100         if (offset) {
197 35           int64_t local = epoch + (int64_t)offset * 60;
198 35 100         if (local < MIN_EPOCH || local > MAX_EPOCH)
    100          
199 2           croak("Parameter 'time' is out of range for the given offset");
200             }
201              
202 122 100         if (fmt == TSTR_FORMAT_RFC5280) {
203 4 100         fmt = (epoch < EPOCH_20500101) ? TSTR_FORMAT_ASN1UT : TSTR_FORMAT_ASN1GT;
204 4           nanosecond = 0;
205 4           offset = 0;
206 4           precision = -1;
207             }
208              
209 122           tstr_datetime_from_epoch(&dt, epoch, offset, nanosecond);
210              
211 122 100         (void)SvUPGRADE(TARG, SVt_PV);
212 122 100         (void)SvGROW(TARG, 30);
    100          
213 122           SvCUR_set(TARG, 0);
214 122           SvPOK_only(TARG);
215              
216 122 50         if (!tstr_time2str(aTHX_ TARG, &dt, precision, fmt))
217 0           croak("Parameter 'format' does not support time2str");
218 122 50         PUSHTARG;
219              
220             void
221             str2time(...)
222             PREINIT:
223             dMY_CXT;
224 185           tstr_format_t fmt = TSTR_FORMAT_RFC3339;
225 185           int pivot_year = -1;
226 185           int precision = -1;
227             tstr_parsed_t parsed;
228             int i;
229             PPCODE:
230 185 100         if (items < 1 || !(items & 1))
    100          
231 2           croak("Usage: str2time(string [, format => 'RFC3339' ])");
232              
233 424 100         for (i = 1; i < items; i += 2) {
234             const char *key;
235             STRLEN klen;
236             SV *val;
237              
238 243           key = SvPV_const(ST(i), klen);
239 243           val = ST(i + 1);
240              
241 243           switch (tstr_param_from_string(key, klen)) {
242 181           case TSTR_PARAM_FORMAT:
243 181           fmt = tstr_sv_format(aTHX_ val);
244 181           break;
245 0           case TSTR_PARAM_PIVOT_YEAR:
246 0           pivot_year = tstr_sv_pivot_year(aTHX_ val);
247 0           break;
248 62           case TSTR_PARAM_PRECISION:
249 62           precision = tstr_sv_precision(aTHX_ val);
250 60           break;
251 0           default:
252 0           croak("Unrecognised named parameter: '%"SVf"'", ST(i));
253             }
254             }
255              
256 181           tstr_parse(aTHX_ ST(0), fmt, pivot_year,
257             MY_CXT.regexps, &MY_CXT.keys, &parsed);
258              
259 181 100         if (!(parsed.flags & TSTR_PARSED_HAS_OFFSET))
260 2           tstr_croak("Unable to convert: timestamp string without a UTC designator or numeric offset");
261              
262             {
263 179           int hour = parsed.hour;
264              
265 179 50         if (parsed.flags & TSTR_PARSED_HAS_MERIDIEM)
266 0           hour = hour % 12 + parsed.meridiem;
267              
268 179           uint32_t rdn = tstr_calendar_ymd_to_rdn(parsed.year, parsed.month, parsed.day);
269 179           int64_t sod = ((int64_t)hour * 60 + parsed.minute) * 60 + parsed.second;
270 179           int64_t epoch = ((int64_t)rdn - TSTR_CALENDAR_RDN_UNIX_EPOCH) * 86400
271 179           + sod - (int64_t)parsed.offset * 60;
272              
273 179 100         if (parsed.flags & TSTR_PARSED_HAS_NANOSECOND) {
274 61 100         int scale_exp = (precision >= 0) ? precision : DEFAULT_PRECISION;
275 61           NV scale = Perl_pow(10.0, (NV)scale_exp);
276 61           NV fraction = Perl_floor((NV)parsed.nanosecond * scale / NANOS_PER_SECOND) / scale;
277 61           mPUSHn((NV)epoch + fraction);
278             } else {
279             #if IVSIZE == 4
280             mPUSHn((NV)epoch);
281             #else
282 118           mPUSHi(epoch);
283             #endif
284             }
285             }
286              
287             void
288             str2date(...)
289             PREINIT:
290             dMY_CXT;
291 781           tstr_format_t fmt = TSTR_FORMAT_RFC3339;
292 781           int pivot_year = -1;
293             tstr_parsed_t parsed;
294             HV *result;
295             int i;
296             PPCODE:
297 781 100         if (items < 1 || !(items & 1))
    100          
298 2           croak("Usage: str2date(string [, format => 'RFC3339' ])");
299              
300 1554 100         for (i = 1; i < items; i += 2) {
301             const char *key;
302             STRLEN klen;
303             SV *val;
304              
305 779           key = SvPV_const(ST(i), klen);
306 779           val = ST(i + 1);
307              
308 779           switch (tstr_param_from_string(key, klen)) {
309 770           case TSTR_PARAM_FORMAT:
310 770           fmt = tstr_sv_format(aTHX_ val);
311 769           break;
312 8           case TSTR_PARAM_PIVOT_YEAR:
313 8           pivot_year = tstr_sv_pivot_year(aTHX_ val);
314 6           break;
315 1           default:
316 1           croak("Unrecognised named parameter: '%"SVf"'", ST(i));
317             }
318             }
319              
320 775           tstr_parse(aTHX_ ST(0), fmt, pivot_year,
321             MY_CXT.regexps, &MY_CXT.keys, &parsed);
322              
323 760 50         if (GIMME_V == G_ARRAY) {
324             int n;
325 0 0         EXTEND(SP, tstr_parsed_field_count(&parsed) * 2);
    0          
326 0           n = tstr_sv_parsed_to_stack(aTHX_ &parsed, &MY_CXT.keys, SP);
327 0           SP += n;
328             } else {
329 760           HV *result = tstr_sv_parsed_to_hv(aTHX_ &parsed, &MY_CXT.keys);
330 760           mPUSHs(newRV_noinc((SV *)result));
331             }
332              
333             MODULE = Time::Str PACKAGE = Time::Str::Token
334              
335             PROTOTYPES: DISABLE
336              
337             void
338             parse_day(...)
339             PREINIT:
340             const char *src;
341             STRLEN len;
342             int value;
343             PPCODE:
344 82 100         if (items != 1)
345 1           croak("Usage: parse_day(string)");
346 81           src = SvPV_const(ST(0), len);
347 81 100         if (!tstr_token_parse_day(src, len, &value))
348 6           tstr_token_croakf("Unable to parse: day is invalid");
349 75           mPUSHi(value);
350              
351             void
352             parse_day_name(...)
353             PREINIT:
354             const char *src;
355             STRLEN len;
356             int value;
357             PPCODE:
358 26 100         if (items != 1)
359 1           croak("Usage: parse_day_name(string)");
360 25           src = SvPV_const(ST(0), len);
361 25 100         if (!tstr_token_parse_day_name(src, len, &value))
362 4           tstr_token_croakf("Unable to parse: day name is invalid");
363 21           mPUSHi(value);
364              
365             void
366             parse_month(...)
367             PREINIT:
368             const char *src;
369             STRLEN len;
370             int value;
371             PPCODE:
372 73 100         if (items != 1)
373 1           croak("Usage: parse_month(string)");
374 72           src = SvPV_const(ST(0), len);
375 72 100         if (!tstr_token_parse_month(src, len, &value))
376 6           tstr_token_croakf("Unable to parse: month is invalid");
377 66           mPUSHi(value);
378              
379             void
380             parse_meridiem(...)
381             PREINIT:
382             const char *src;
383             STRLEN len;
384             int value;
385             PPCODE:
386 15 100         if (items != 1)
387 1           croak("Usage: parse_meridiem(string)");
388 14           src = SvPV_const(ST(0), len);
389 14 100         if (!tstr_token_parse_meridiem(src, len, &value))
390 4           tstr_token_croakf("Unable to parse: meridiem is invalid");
391 10           mPUSHi(value);
392              
393             void
394             parse_tz_offset(...)
395             PREINIT:
396             const char *src;
397             STRLEN len;
398             int value;
399             PPCODE:
400 45 100         if (items != 1)
401 1           croak("Usage: parse_tz_offset(string)");
402 44           src = SvPV_const(ST(0), len);
403 44 100         if (!tstr_token_parse_tz_offset(src, len, &value))
404 12           tstr_token_croakf("Unable to parse: timezone offset is invalid");
405 32           mPUSHi(value);
406              
407              
408             MODULE = Time::Str PACKAGE = Time::Str::Calendar
409              
410             PROTOTYPES: DISABLE
411              
412             void
413             leap_year(...)
414             PPCODE:
415 30 100         if (items != 1)
416 1           croak("Usage: leap_year(year)");
417 29 100         if (tstr_calendar_leap_year((int)SvIV(ST(0))))
418 16           XSRETURN_YES;
419 13           XSRETURN_NO;
420              
421             void
422             month_days(...)
423             PREINIT:
424             int y, m;
425             PPCODE:
426 18 100         if (items != 2)
427 1           croak("Usage: month_days(year, month)");
428 17           y = (int)SvIV(ST(0));
429 17           m = tstr_sv_month(aTHX_ ST(1));
430 15           mPUSHi(tstr_calendar_month_days(y, m));
431              
432             void
433             valid_ymd(...)
434             PPCODE:
435 44 100         if (items != 3)
436 1           croak("Usage: valid_ymd(year, month, day)");
437 43 100         if (tstr_calendar_valid_ymd((int)SvIV(ST(0)), (int)SvIV(ST(1)), (int)SvIV(ST(2))))
438 23           XSRETURN_YES;
439 20           XSRETURN_NO;
440              
441             void
442             ymd_to_rdn(...)
443             PREINIT:
444             int y, m, d;
445             PPCODE:
446 52 100         if (items != 3)
447 1           croak("Usage: ymd_to_rdn(year, month, day)");
448 51           tstr_sv_ymd(aTHX_ ST(0), ST(1), ST(2), &y, &m, &d);
449 45           mPUSHi((IV)tstr_calendar_ymd_to_rdn(y, m, d));
450              
451             void
452             ymd_to_doy(...)
453             PREINIT:
454             int y, m, d;
455             PPCODE:
456 31 100         if (items != 3)
457 1           croak("Usage: ymd_to_doy(year, month, day)");
458 30           tstr_sv_ymd(aTHX_ ST(0), ST(1), ST(2), &y, &m, &d);
459 24           mPUSHi(tstr_calendar_ymd_to_doy(y, m, d));
460              
461             void
462             yd_to_md(...)
463             PREINIT:
464             int y, doy, m, d;
465             PPCODE:
466 95 100         if (items != 2)
467 1           croak("Usage: yd_to_md(year, day)");
468 94           tstr_sv_yd(aTHX_ ST(0), ST(1), &y, &doy);
469 90           tstr_calendar_yd_to_md(y, doy, &m, &d);
470 90 50         EXTEND(SP, 2);
471 90           mPUSHi(m);
472 90           mPUSHi(d);
473              
474             void
475             rdn_to_ymd(...)
476             PREINIT:
477             IV rdn;
478             int y, m, d;
479             PPCODE:
480 18 100         if (items != 1)
481 1           croak("Usage: rdn_to_ymd(rdn)");
482 17           rdn = SvIV(ST(0));
483 17 100         if (rdn < TSTR_CALENDAR_RDN_MIN || rdn > TSTR_CALENDAR_RDN_MAX)
    100          
484 2           croak("Parameter 'rdn' is out of range");
485 15           tstr_calendar_rdn_to_ymd((uint32_t)rdn, &y, &m, &d);
486 15 50         EXTEND(SP, 3);
487 15           mPUSHi(y);
488 15           mPUSHi(m);
489 15           mPUSHi(d);
490              
491             void
492             rdn_to_dow(...)
493             PREINIT:
494             IV rdn;
495             PPCODE:
496 33 100         if (items != 1)
497 1           croak("Usage: rdn_to_dow(rdn)");
498 32           rdn = SvIV(ST(0));
499 32 100         if (rdn < TSTR_CALENDAR_RDN_MIN || rdn > TSTR_CALENDAR_RDN_MAX)
    100          
500 2           croak("Parameter 'rdn' is out of range");
501 30           mPUSHi(tstr_calendar_rdn_to_dow((uint32_t)rdn));
502              
503             void
504             ymd_to_dow(...)
505             PREINIT:
506             int y, m, d;
507             PPCODE:
508 59 100         if (items != 3)
509 1           croak("Usage: ymd_to_dow(year, month, day)");
510 58           tstr_sv_ymd(aTHX_ ST(0), ST(1), ST(2), &y, &m, &d);
511 52           mPUSHi(tstr_calendar_ymd_to_dow(y, m, d));
512              
513             void
514             nth_dow_in_month(...)
515             PREINIT:
516             int y, m, ord, dow, day;
517             PPCODE:
518 306 100         if (items != 4)
519 1           croak("Usage: nth_dow_in_month(year, month, ord, dow)");
520 305           y = (int)SvIV(ST(0));
521 305           m = (int)SvIV(ST(1));
522 305           ord = (int)SvIV(ST(2));
523 305           dow = (int)SvIV(ST(3));
524 305 100         if (y < 1 || y > 9999)
    100          
525 2           croak("Parameter 'year' is out of range [1, 9999]");
526 303 100         if (m < 1 || m > 12)
    100          
527 2           croak("Parameter 'month' is out of range [1, 12]");
528 301 100         if (ord < -4 || ord > 4 || ord == 0)
    100          
    100          
529 3           croak("Parameter 'ord' is out of range [-4, -1] or [1, 4]");
530 298 100         if (dow < 1 || dow > 7)
    100          
531 2           croak("Parameter 'dow' is out of range [1, 7]");
532 296           mPUSHi(tstr_calendar_nth_dow_in_month(y, m, ord, dow));
533              
534             void
535             resolve_century(...)
536             PREINIT:
537             int year, pivot_year;
538             PPCODE:
539 22 100         if (items != 2)
540 1           croak("Usage: resolve_century(year, pivot_year)");
541 21           year = (int)SvIV(ST(0));
542 21 100         if (year < 0 || year > 99)
    100          
543 2           croak("Parameter 'year' is out of range [0, 99]");
544 19           pivot_year = tstr_sv_pivot_year(aTHX_ ST(1));
545 17           mPUSHi(tstr_calendar_resolve_century(year, pivot_year));
546              
547             MODULE = Time::Str PACKAGE = Time::Str::Time
548              
549             PROTOTYPES: DISABLE
550              
551             void
552             valid_hms(...)
553             PPCODE:
554 12 100         if (items != 3)
555 1           croak("Usage: valid_hms(hour, minute, second)");
556 11 100         if (tstr_time_valid_hms((int)SvIV(ST(0)), (int)SvIV(ST(1)), (int)SvIV(ST(2))))
557 5           XSRETURN_YES;
558 6           XSRETURN_NO;
559              
560             void
561             valid_hms60(...)
562             PPCODE:
563 10 100         if (items != 3)
564 1           croak("Usage: valid_hms60(hour, minute, second)");
565 9 100         if (tstr_time_valid_hms60((int)SvIV(ST(0)), (int)SvIV(ST(1)), (int)SvIV(ST(2))))
566 3           XSRETURN_YES;
567 6           XSRETURN_NO;
568              
569             void
570             timegm_modern(...)
571             PREINIT:
572             int y, m, d, H, M, S;
573             PPCODE:
574 394 100         if (items != 6)
575 1           croak("Usage: timegm_modern(sec, min, hour, mday, mon, year)");
576 393           S = (int)SvIV(ST(0));
577 393           M = (int)SvIV(ST(1));
578 393           H = (int)SvIV(ST(2));
579 393           d = (int)SvIV(ST(3));
580 393           m = (int)SvIV(ST(4));
581 393           y = (int)SvIV(ST(5));
582 393 100         if (y < 1 || y > 9999)
    100          
583 2           croak("Parameter 'year' is out of range [1, 9999]");
584 391 100         if (m < 1 || m > 12)
    100          
585 2           croak("Parameter 'month' is out of range [1, 12]");
586 389 100         if (d < 1 || d > tstr_calendar_month_days(y, m))
    100          
587 3           croak("Parameter 'day' is out of range");
588 386 50         if (H < 0 || H > 23)
    100          
589 1           croak("Parameter 'hour' is out of range [0, 23]");
590 385 50         if (M < 0 || M > 59)
    100          
591 1           croak("Parameter 'minute' is out of range [0, 59]");
592 384 50         if (S < 0 || S > 59)
    100          
593 1           croak("Parameter 'second' is out of range [0, 59]");
594             #if IVSIZE >= 8
595 383           mPUSHi((IV)tstr_time_timegm(y, m, d, H, M, S));
596             #else
597             mPUSHn((NV)tstr_time_timegm(y, m, d, H, M, S));
598             #endif
599              
600             void
601             gmtime_modern(...)
602             PREINIT:
603             int y, m, d, H, M, S, wday, yday;
604             int64_t epoch;
605             PPCODE:
606 22 100         if (items != 1)
607 1           croak("Usage: gmtime_modern(time)");
608             #if IVSIZE >= 8
609 21           epoch = (int64_t)SvIV(ST(0));
610             #else
611             epoch = (int64_t)SvNV(ST(0));
612             #endif
613 21 100         if (epoch < TSTR_TIME_EPOCH_MIN || epoch > TSTR_TIME_EPOCH_MAX)
    100          
614 2           croak("Parameter 'time' is out of range");
615 19           tstr_time_gmtime(epoch, &y, &m, &d, &H, &M, &S, &wday, &yday);
616 19 50         EXTEND(SP, 9);
617 19           mPUSHi(S);
618 19           mPUSHi(M);
619 19           mPUSHi(H);
620 19           mPUSHi(d);
621 19           mPUSHi(m);
622 19           mPUSHi(y);
623 19           mPUSHi(wday);
624 19           mPUSHi(yday);
625 19           mPUSHi(0);
626              
627             void
628             gmtime_year(...)
629             PREINIT:
630             int y;
631             int64_t epoch;
632             PPCODE:
633 84 50         if (items != 1)
634 0           croak("Usage: gmtime_modern(time)");
635             #if IVSIZE >= 8
636 84           epoch = (int64_t)SvIV(ST(0));
637             #else
638             epoch = (int64_t)SvNV(ST(0));
639             #endif
640 84 50         if (epoch < TSTR_TIME_EPOCH_MIN || epoch > TSTR_TIME_EPOCH_MAX)
    50          
641 0           croak("Parameter 'time' is out of range");
642 84           tstr_time_gmtime(epoch, &y, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
643 84 50         EXTEND(SP, 1);
644 84           mPUSHi(y);
645              
646             void
647             timegm_posix(...)
648             PREINIT:
649             int y, m, d, H, M, S;
650             PPCODE:
651 10 100         if (items != 6)
652 1           croak("Usage: timegm_posix(sec, min, hour, mday, mon, year)");
653 9           S = (int)SvIV(ST(0));
654 9           M = (int)SvIV(ST(1));
655 9           H = (int)SvIV(ST(2));
656 9           d = (int)SvIV(ST(3));
657 9           m = (int)SvIV(ST(4));
658 9           y = (int)SvIV(ST(5));
659 9 100         if (y < -1899 || y > 8099)
    100          
660 2           croak("Parameter 'year' is out of range [-1899, 8099]");
661 7           y += 1900;
662 7 100         if (m < 0 || m > 11)
    100          
663 2           croak("Parameter 'month' is out of range [0, 11]");
664 5           m += 1;
665 5 50         if (d < 1 || d > tstr_calendar_month_days(y, m))
    50          
666 0           croak("Parameter 'day' is out of range");
667 5 50         if (H < 0 || H > 23)
    50          
668 0           croak("Parameter 'hour' is out of range [0, 23]");
669 5 50         if (M < 0 || M > 59)
    50          
670 0           croak("Parameter 'minute' is out of range [0, 59]");
671 5 50         if (S < 0 || S > 59)
    50          
672 0           croak("Parameter 'second' is out of range [0, 59]");
673             #if IVSIZE >= 8
674 5           mPUSHi((IV)tstr_time_timegm(y, m, d, H, M, S));
675             #else
676             mPUSHn((NV)tstr_time_timegm(y, m, d, H, M, S));
677             #endif
678              
679             MODULE = Time::Str PACKAGE = Time::Str::Util
680              
681             PROTOTYPES: DISABLE
682              
683             void
684             lower_bound(...)
685             PREINIT:
686             AV *av;
687             IV value, lo, hi, mid;
688             PPCODE:
689 22 100         if (items < 2 || items > 4)
    50          
690 1           croak("Usage: lower_bound(array, value [, lo [, hi]])");
691 21 100         if (!SvROK(ST(0)) || SvTYPE(SvRV(ST(0))) != SVt_PVAV)
    50          
692 1           croak("Parameter 'array' must be an array reference");
693 20           av = (AV *)SvRV(ST(0));
694 20           value = SvIV(ST(1));
695             {
696 20           IV len = av_len(av) + 1;
697 20 100         lo = (items >= 3) ? SvIV(ST(2)) : 0;
698 20 100         hi = (items >= 4) ? SvIV(ST(3)) : len;
699 20 50         if (lo < 0 || lo > len)
    50          
700 0           croak("Parameter 'lo' is out of range [0, %" IVdf "]", len);
701 20 50         if (hi < 0 || hi > len)
    50          
702 0           croak("Parameter 'hi' is out of range [0, %" IVdf "]", len);
703 20 50         if (lo > hi)
704 0           croak("Parameter 'lo' must not exceed 'hi'");
705             }
706 65 100         while (lo < hi) {
707 45           mid = (lo + hi) >> 1;
708             {
709 45           SV **elem = av_fetch(av, mid, 0);
710 45 50         if (elem && SvIV(*elem) < value)
    100          
711 16           lo = mid + 1;
712             else
713 29           hi = mid;
714             }
715             }
716 20           mPUSHi(lo);
717              
718             void
719             range_bounds(...)
720             PREINIT:
721             AV *av;
722             IV min_value, max_value, lo, hi, mid, len;
723             PPCODE:
724 13 100         if (items != 3)
725 1           croak("Usage: range_bounds(array, min_value, max_value)");
726 12 100         if (!SvROK(ST(0)) || SvTYPE(SvRV(ST(0))) != SVt_PVAV)
    50          
727 1           croak("Parameter 'array' must be an array reference");
728 11           av = (AV *)SvRV(ST(0));
729 11           min_value = SvIV(ST(1));
730 11           max_value = SvIV(ST(2));
731 11 100         if (min_value > max_value)
732 1           croak("Parameter 'min_value' must not exceed 'max_value'");
733 10           len = av_len(av) + 1;
734             // lower_bound for min_value
735 10           lo = 0;
736 10           hi = len;
737 34 100         while (lo < hi) {
738 24           mid = (lo + hi) >> 1;
739             {
740 24           SV **elem = av_fetch(av, mid, 0);
741 24 50         if (elem && SvIV(*elem) < min_value)
    100          
742 8           lo = mid + 1;
743             else
744 16           hi = mid;
745             }
746             }
747             // linear scan for upper bound
748 10           hi = lo;
749 26 100         while (hi < len) {
750 23           SV **elem = av_fetch(av, hi, 0);
751 23 50         if (elem && SvIV(*elem) <= max_value)
    100          
752 16           hi++;
753             else
754             break;
755             }
756 10 50         EXTEND(SP, 2);
757 10           mPUSHi(lo);
758 10           mPUSHi(hi);
759              
760             void
761             upper_bound(...)
762             PREINIT:
763             AV *av;
764             IV value, lo, hi, mid;
765             PPCODE:
766 78 100         if (items < 2 || items > 4)
    50          
767 1           croak("Usage: upper_bound(array, value [, lo [, hi ]])");
768 77 100         if (!SvROK(ST(0)) || SvTYPE(SvRV(ST(0))) != SVt_PVAV)
    50          
769 1           croak("Parameter 'array' must be an array reference");
770 76           av = (AV *)SvRV(ST(0));
771 76           value = SvIV(ST(1));
772             {
773 76           IV len = av_len(av) + 1;
774 76 100         lo = (items >= 3) ? SvIV(ST(2)) : 0;
775 76 100         hi = (items >= 4) ? SvIV(ST(3)) : len;
776 76 50         if (lo < 0 || lo > len)
    50          
777 0           croak("Parameter 'lo' is out of range [0, %" IVdf "]", len);
778 76 50         if (hi < 0 || hi > len)
    50          
779 0           croak("Parameter 'hi' is out of range [0, %" IVdf "]", len);
780 76 50         if (lo > hi)
781 0           croak("Parameter 'lo' must not exceed 'hi'");
782             }
783 244 100         while (lo < hi) {
784 168           mid = (lo + hi) >> 1;
785             {
786 168           SV **elem = av_fetch(av, mid, 0);
787 168 50         if (elem && SvIV(*elem) <= value)
    100          
788 67           lo = mid + 1;
789             else
790 101           hi = mid;
791             }
792             }
793 76           mPUSHi(lo);
794