DateTime-Lite

 view release on metacpan or  search on metacpan

DateTime-Lite.xs  view on Meta::CPAN

{
    /* See http://www.perlmonks.org/?node_id=274247 for where this silliness comes from */
    return (y % 4) ? 0 : (y % 100) ? 1 : (y % 400) ? 0 : 1;
}

/*-------------------------------------------------------------------
 * dtl_clone_flat_hv( src )
 *
 * Copies a hashref whose values are all scalars (no nested refs).
 * Used by clone() to duplicate the tz and locale blessed hashrefs,
 * and the local_c cache hashref, all of which contain only scalar
 * values at the time of cloning.
 *-------------------------------------------------------------------*/
static HV *
dtl_clone_flat_hv(HV *src)
{
    HV  *dst;
    HE  *entry;

    dst = newHV();

    /* Pre-size to avoid incremental bucket resizing */
    if( HvKEYS(src) > 0 )
        hv_ksplit(dst, HvKEYS(src));

    hv_iterinit(src);
    while( ( entry = hv_iternext(src) ) )
    {
        I32   klen = HeKLEN(entry);
        char *kpv  = HeKEY(entry);
        SV   *val  = HeVAL(entry);

        /* Negate klen for UTF-8 keys (Perl API convention) */
        if( HeKUTF8(entry) )
            klen = -klen;

        hv_store(dst, kpv, klen, newSVsv(val), HeHASH(entry));
    }

    return dst;
}

MODULE = DateTime::Lite    PACKAGE = DateTime::Lite

PROTOTYPES: ENABLE

# Rata Die (RD) <-> Calendar conversions
void
_rd2ymd(self, d, extra = 0)
    IV d;
    IV extra;

    PREINIT:
        IV y, m;
        IV c;
        IV quarter;
        IV yadj = 0;
        IV dow, doy, doq;
        IV rd_days;

    PPCODE:
        rd_days = d;

        d += MARCH_1;

        if( d <= 0 )
        {
            yadj = -1 * (((-1 * d) / DAYS_PER_400_YEARS) + 1);
            d -= yadj * DAYS_PER_400_YEARS;
        }

        /* c is century */
        c =  ((d * 4) - 1) / DAYS_PER_400_YEARS;
        d -= c * DAYS_PER_400_YEARS / 4;
        y =  ((d * 4) - 1) / DAYS_PER_4_YEARS;
        d -= y * DAYS_PER_4_YEARS / 4;
        m =  ((d * 12) + 1093) / 367;
        d -= ((m * 367) - 1094) / 12;
        y += (c * 100) + (yadj * 400);

        if( m > 12 )
        {
            ++y;
            m -= 12;
        }

        EXTEND(SP, extra ? 7 : 3);
        mPUSHi(y);
        mPUSHi(m);
        mPUSHi(d);

        if( extra )
        {
            quarter = ( ( 1.0 / 3.1 ) * m ) + 1;

            dow = rd_days % 7;
            if( dow <= 0 )
            {
                dow += 7;
            }

            mPUSHi(dow);

            if(_real_is_leap_year(y) )
            {
                doy = PREVIOUS_MONTH_DOLY[m - 1] + d;
                doq = doy - PREVIOUS_MONTH_DOLY[ (3 * quarter) - 3 ];
            }
            else
            {
                doy = PREVIOUS_MONTH_DOY[m - 1] + d;
                doq = doy - PREVIOUS_MONTH_DOY[ (3 * quarter ) - 3 ];
            }

            mPUSHi(doy);
            mPUSHi(quarter);
            mPUSHi(doq);
        }

void
_ymd2rd(self, y, m, d)
    IV y;
    IV m;
    IV d;

    PREINIT:
        IV adj;

    PPCODE:
        if( m <= 2 )
        {
            adj = (14 - m) / 12;
            y -= adj;
            m += 12 * adj;
        }
        else if( m > 14 )
        {
            adj = (m - 3) / 12;
            y += adj;
            m -= 12 * adj;
        }

        if( y < 0 )
        {
            adj = (399 - y) / 400;
            d -= DAYS_PER_400_YEARS * adj;
            y += 400 * adj;
        }

        d += (m * 367 - 1094) /
            12 + y % 100 * DAYS_PER_4_YEARS /
            4 + (y / 100 * 36524 + y / 400) - MARCH_1;

        EXTEND(SP, 1);
        mPUSHi(d);

# Time component decomposition
void
_seconds_as_components(self, secs, utc_secs = 0, secs_modifier = 0)
    IV secs;
    IV utc_secs;
    IV secs_modifier;

    PREINIT:
        IV h, m, s;

    PPCODE:
        secs -= secs_modifier;

        h = secs / 3600;
        secs -= h * 3600;

        m = secs / 60;

        s = secs - (m * 60);

        if( utc_secs >= SECONDS_PER_DAY )
        {
            if( utc_secs >= SECONDS_PER_DAY + 1 )
            {
                croak("Invalid UTC RD seconds value: %s",
                      SvPV_nolen(newSViv(utc_secs)));
            }

            s += (utc_secs - SECONDS_PER_DAY) + 60;
            m = 59;
            h--;

            if( h < 0 )
            {
                h = 23;
            }
        }

        EXTEND(SP, 3);
        mPUSHi(h);
        mPUSHi(m);
        mPUSHi(s);

void
_time_as_seconds(self, h, m, s)
    IV h;
    IV m;
    IV s;

    PPCODE:
        EXTEND(SP, 1);
        mPUSHi(h * 3600 + m * 60 + s);

# Leap year / leap second helpers
void
_is_leap_year(self, y)
    IV y;

    PPCODE:
        EXTEND(SP, 1);
        mPUSHi(_real_is_leap_year(y));

void
_day_length(self, utc_rd)
    IV utc_rd;

    PPCODE:
        IV day_length;
        SET_DAY_LENGTH(utc_rd, day_length);

        EXTEND(SP, 1);
        mPUSHi(day_length);

void
_day_has_leap_second(self, utc_rd)
    IV utc_rd;

    PPCODE:
        IV day_length;
        SET_DAY_LENGTH(utc_rd, day_length);

        EXTEND(SP, 1);
        mPUSHi(day_length > SECONDS_PER_DAY ? 1 : 0);

void
_accumulated_leap_seconds(self, utc_rd)
    IV utc_rd;

    PPCODE:
        IV leap_seconds;
        SET_LEAP_SECONDS(utc_rd, leap_seconds);

        EXTEND(SP, 1);
        mPUSHi(leap_seconds);

#ifdef dtl_isfinite

# Normalisation (TAI / leap-second-aware)
void
_normalize_tai_seconds(self, days, secs)
    SV* days;
    SV* secs;

    PPCODE:
        if( dtl_isfinite(SvNV(days)) && dtl_isfinite(SvNV(secs)) )
        {
            IV d = SvIV(days);
            IV s = SvIV(secs);
            IV adj;

            if( s < 0 )
            {
                adj = (s - (SECONDS_PER_DAY - 1)) / SECONDS_PER_DAY;
            }
            else
            {
                adj = s / SECONDS_PER_DAY;
            }

            d += adj;
            s -= adj * SECONDS_PER_DAY;

            sv_setiv(days, (IV) d);
            sv_setiv(secs, (IV) s);
        }

void
_normalize_leap_seconds(self, days, secs)
    SV* days;
    SV* secs;

    PPCODE:
        if( dtl_isfinite(SvNV(days)) && dtl_isfinite(SvNV(secs)) )
        {
            IV d = SvIV(days);
            IV s = SvIV(secs);
            IV day_length;

            while( s < 0 )
            {
                SET_DAY_LENGTH(d - 1, day_length);

                s += day_length;
                d--;
            }

            SET_DAY_LENGTH(d, day_length);

            while( s > day_length - 1 )
            {
                s -= day_length;
                d++;
                SET_DAY_LENGTH(d, day_length);
            }

            sv_setiv(days, (IV) d);
            sv_setiv(secs, (IV) s);
        }

# Additional XS functions (not in original DateTime)

# Compute Unix epoch from utc_rd_days + utc_rd_secs directly in C.
# Returns IV (integer seconds).  Caller adds nanoseconds if needed.
IV
_rd_to_epoch(self, rd_days, rd_secs)
    IV rd_days;
    IV rd_secs;

    CODE:
        RETVAL = (rd_days - UNIX_EPOCH_RD_DAYS) * SECONDS_PER_DAY + rd_secs;

    OUTPUT:
        RETVAL

# Compute utc_rd_days and utc_rd_secs from a Unix epoch integer.
# Returns a 2-element list: (rd_days, rd_secs).
void
_epoch_to_rd(self, epoch)
    IV epoch;

    PREINIT:
        IV d, s;

    PPCODE:
        d = epoch / SECONDS_PER_DAY;
        s = epoch - d * SECONDS_PER_DAY;

        if( s < 0 )
        {
            d--;
            s += SECONDS_PER_DAY;
        }

        d += UNIX_EPOCH_RD_DAYS;

        EXTEND(SP, 2);
        mPUSHi(d);
        mPUSHi(s);

# In-place nanosecond normalisation.
# Modifies the two SV* scalars passed in (seconds, nanoseconds), carrying over-/underflow
# between them.
void
_normalize_nanoseconds(self, secs, nanosecs)
    SV* secs;
    SV* nanosecs;

    PPCODE:
        {
            IV s  = SvIV(secs);
            IV ns = SvIV(nanosecs);
            IV overflow;

            if( ns < 0 )
            {
                overflow = 1 + (-ns - 1) / MAX_NANOSECONDS;
                ns += overflow * MAX_NANOSECONDS;
                s  -= overflow;
            }
            else if( ns >= MAX_NANOSECONDS )
            {
                overflow = ns / MAX_NANOSECONDS;
                ns -= overflow * MAX_NANOSECONDS;
                s  += overflow;
            }

            sv_setiv(secs,      (IV) s);
            sv_setiv(nanosecs,  (IV) ns);
        }

# Fast three-way UTC comparison: returns -1, 0, or 1.
# Compares (rd_days1, rd_secs1, rd_ns1) vs (rd_days2, rd_secs2, rd_ns2).
IV
_compare_rd(self, rd_days1, rd_secs1, rd_ns1, rd_days2, rd_secs2, rd_ns2)
    IV rd_days1;
    IV rd_secs1;
    IV rd_ns1;
    IV rd_days2;
    IV rd_secs2;
    IV rd_ns2;

    CODE:
        if( rd_days1 != rd_days2 )
        {
            RETVAL = (rd_days1 > rd_days2) ? 1 : -1;
        }
        else if( rd_secs1 != rd_secs2 )
        {
            RETVAL = (rd_secs1 > rd_secs2) ? 1 : -1;
        }
        else if( rd_ns1 != rd_ns2 )
        {
            RETVAL = (rd_ns1 > rd_ns2) ? 1 : -1;
        }
        else
        {
            RETVAL = 0;
        }

    OUTPUT:
        RETVAL

#endif /* ifdef dtl_isfinite */

#-------------------------------------------------------------------
# clone()
#
# Returns a two-level deep copy of the DateTime::Lite object.



( run in 2.490 seconds using v1.01-cache-2.11-cpan-71847e10f99 )