Base R date-time manipulation with POSIXlt

POSIXlt: date components as mutable fields

POSIXlt is a list where the date components ($year, $mon, $mday, etc.) are plain integers you can assign to. Normalizing back through as.POSIXct applies standard mktime overflow rules — month/day overflow cascades.

No string substitution, no regex. Adding a year is lt$year <- lt$year + 1.

Cascade semantics vs. clamping

Month/day overflow rolls forward (POSIX mktime behavior). Adding 13 months to February advances the year. If you need lubridate-style clamp (Feb 29 -> Feb 28 instead of March 1), handle that explicitly — base R won’t do it for you.

Timezone: CET / CEST from IANA zones

You cannot parse CEST as an input timezone abbreviation (that’s the unknown timezone 'CEST' error). But you don’t need to. Use a location-based IANA zone and R derives the correct DST abbreviation and offset from the database.

The distinction: CET/CEST are output labels, not input zones. Europe/Madrid (or Europe/Paris, etc.) is the zone. Whether a given instant prints as CET or CEST is resolved automatically from the date. You don’t check %m to pick the abbreviation — R does that for you.

Parsing flexible date strings

When input may or may not include a time component, anytime/anydate works, but base R handles it too:

The gap vs. lubridate

The one real gap is ergonomics — no %m+% operator, no clamping, and you carry the cascade semantics yourself. But the functionality is all in POSIXlt field mutation plus IANA zone names. Zero external dependencies.