SAS: partial --DTC / character dates; impute with INTNX beginning/end or a fixed mid day
Clinical data often arrive as incomplete ISO-like strings: year only (2025) or year-month (2024-04). Derivation rules usually push these to the first, middle, or last day of the known period. Month bounds matter most for year-month; year-only is the same idea at year scale.
Always follow the study imputation convention and record how far you imputed (for example CDISC --DTF) when required. Examples below use generic STD-001-* subjects and one explicit mid rule.
Quick reference
library(dplyr)library(lubridate)# year-only "2025" -> first / mid / last day of yearymd(paste0(year_txt, "-01-01"))ymd(paste0(year_txt, "-06-30")) # common mid-year conventionymd(paste0(year_txt, "-12-31"))# year-month "2024-04" -> first / mid / last day of monthymd(paste0(ym_txt, "-01"))ymd(paste0(ym_txt, "-15")) # common mid-month convention(ceiling_date(ymd(paste0(ym_txt, "-01")), "month") -days(1)) |>as.Date()
R (after parsing the known part)
SAS idea
first day of month/year
impute to period start (INTNX beginning)
mid day (often 15 / 30 Jun)
sponsor mid-point rule
last day of month/year
impute to period end (INTNX end)
Traps: document the mid rule (15 vs true midpoint). ceiling_date(..., "month") alone is next month start, not month end. Missing completely stays missing. Keep an imputation flag (--DTF) when the standard requires it.
Worked examples below.
Year-month to first, mid, last
Three partial year-month values, including February in a leap year.
library(dplyr)
Attaching package: 'dplyr'
The following objects are masked from 'package:stats':
filter, lag
The following objects are masked from 'package:base':
intersect, setdiff, setequal, union
library(lubridate)
Attaching package: 'lubridate'
The following objects are masked from 'package:base':
date, intersect, setdiff, union
First day matches SAS INTNX('month', ..., 0, 'beginning') on a real date in that month. Last day matches INTNX(..., 'end'). Day 15 is a common mid-month convention; some protocols use other mid rules. Pick one and keep it consistent.
ImportantMonth end is not bare ceiling_date
ceiling_date(start, "month") is the first day of the next month. Last day of the current month is that value minus one day.
Year-only to first, mid, last
Year-only strings are easier: fixed calendar anchors.
30 June is a common mid-year convention (half-year boundary). Some conventions use 1 July or day 183. Document the choice the same way you document day 15 for months.
SAS analogs: INTNX('year', ..., 0, 'beginning') / 'end', or a fixed mid date constructed from the year.
Choosing first vs last vs mid
Situation
Typical choice
Conservative "earliest possible" onset
first of period
Conservative "latest possible"
last of period
Neutral / midpoint rule in SAP
mid (day 15 / 30 Jun or protocol rule)
Do not invent a mid rule in production code. Read the SAP or derivation specification. When CDISC date imputation flags apply, set --DTF to show which parts were imputed (for example D when day was imputed, M when month and day were imputed). Exact flag codes follow the standard and the study metadata.
Missing or unusable partial strings stay missing after failed parses. Do not silently coerce blank --DTC to period start.