# Get the current date
current_date <- Sys.Date()
print(current_date)[1] "2025-12-17"
# Get the current date and time
current_datetime <- Sys.time()
print(current_datetime)[1] "2025-12-17 15:42:18 UTC"
Date and time manipulation is a common task in data analysis. R provides several base functions to work with dates and times. Here are some of the most commonly used base R functions for handling date and time data:
# Get the current date
current_date <- Sys.Date()
print(current_date)[1] "2025-12-17"
# Get the current date and time
current_datetime <- Sys.time()
print(current_datetime)[1] "2025-12-17 15:42:18 UTC"
| Code | Meaning | Example (format(dt, ...)) |
|---|---|---|
%Y |
4-digit year | "2025" |
%y |
2-digit year (00–99) | "25" |
%C |
Century (year / 100, 00–99) | "20" (for 2025; OS-dep.) |
| Code | Meaning | Example |
|---|---|---|
%m |
Month number (01–12) | "11" |
%b |
Abbrev. month name (locale) | "Nov" |
%B |
Full month name (locale) | "November" |
%h |
Same as %b (abbrev. month name) |
"Nov" |
| Code | Meaning | Example |
|---|---|---|
%d |
Day of month (01–31) | "14" |
%e |
Day of month (1–31, padded with space) | "14" (or " 7" for 7th; OS-dep.) |
%j |
Day of year (001–366) | "318" |
| Code | Meaning | Example |
|---|---|---|
%a |
Abbrev. weekday name (locale) | "Fri" |
%A |
Full weekday name (locale) | "Friday" |
%w |
Weekday number (0–6, 0 = Sunday) | "5" |
%u |
ISO weekday number (1–7, 1 = Monday) (OS-dep.) | "5" (Friday) |
| Code | Meaning | Example |
|---|---|---|
%H |
Hour (00–23) | "14" |
%I |
Hour (01–12) | "02" |
%M |
Minute (00–59) | "30" |
%S |
Second (00–61, allows for leap) | "05" |
%p |
AM/PM indicator (locale) | "PM" |
###Locale-dependent “full date/time” codes
###These depend on your system locale (language + region):
| Code | Meaning | Example |
|---|---|---|
%c |
Date and time representation | "Fri Nov 14 16:05:30 2025" |
%x |
Date representation | "11/14/25" (varies by locale) |
%X |
Time representation | "16:05:30" |
# Create a Date object
dt <- as.Date("2025-11-14")
# Create a POSIXct object (date-time)
dt <- as.POSIXct("2025-11-14 16:05:30")
# Format examples
format(dt, "%Y-%m-%d") # e.g. "2025-11-14"[1] "2025-11-14"
format(dt, "%m/%d/%y") # e.g. "11/14/25"[1] "11/14/25"
format(dt, "%B %d, %Y") # e.g. "November 14, 2025"[1] "November 14, 2025"
format(dt, "%c") # e.g. "Fri Nov 14 16:05:30 2025"[1] "Fri Nov 14 16:05:30 2025"
format(dt, "%x") # e.g. "11/14/25" (depends on locale)[1] "11/14/25"
format(dt, "%X") # e.g. "16:05:30"[1] "16:05:30"
| SAS informat | R parsing |
|---|---|
ddmmyy10. |
as.Date(x, "%d/%m/%Y") |
ddmmyyd10. |
as.Date(x, "%d-%m-%Y") |
ddmmyyp10. |
as.Date(x, "%d.%m.%Y") |
mmddyy10. |
as.Date(x, "%m/%d/%Y") |
date9. |
as.Date(x, "%d%b%Y") |
julian7. |
as.Date(strptime(x, "%Y%j")) |
time8. |
as.POSIXct(x, "%H:%M:%S") |
time10. |
as.POSIXct(x, "%I:%M:%S%p") (if am/pm) |
datetime18. |
as.POSIXct(x, "%d%b%Y:%H:%M:%S") |
datetime20. |
as.POSIXct(x, "%d%b%Y:%I:%M:%S%p") |
| SAS format | Meaning | R equivalent |
|---|---|---|
worddate18. |
Month date, year (e.g., February 23, 2003) | format(d, "%B %d, %Y") |
weekdate24. |
Weekday, month date, year | format(d, "%A, %B %d, %Y") |
year. |
numeric year | as.integer(format(d, "%Y")) |
month. |
month number (1–12) | as.integer(format(d, "%m")) |
day. |
day of month | as.integer(format(d, "%d")) |
weekday. |
day of week (1–7) | as.integer(format(d, "%u")) (Mon = 1) |
# Convert a character string to a Date object
date_string <- "2023-10-01"
date_object <- as.Date(date_string)
print(date_object)[1] "2023-10-01"
# Convert a character string to a POSIXct object (date-time)
datetime_string <- "2023-10-01 12:34:56"
datetime_object <- as.POSIXct(datetime_string)
print(datetime_object)[1] "2023-10-01 12:34:56 UTC"
# Extract components from a Date object
year <- format(date_object, "%Y")
month <- format(date_object, "%m")
day <- format(date_object, "%d")
print(paste("Year:", year, "Month:", month, "Day:", day))[1] "Year: 2023 Month: 10 Day: 01"
# Extract components from a POSIXct object
hour <- format(datetime_object, "%H")
minute <- format(datetime_object, "%M")
second <- format(datetime_object, "%S")
print(paste("Hour:", hour, "Minute:", minute, "Second:", second))[1] "Hour: 12 Minute: 34 Second: 56"
# Perform date arithmetic
tomorrow <- current_date + 1
yesterday <- current_date - 1
print(paste("Tomorrow:", tomorrow, "Yesterday:", yesterday))[1] "Tomorrow: 2025-12-18 Yesterday: 2025-12-16"
# Calculate the difference between two dates
date_diff <- as.numeric(difftime(current_date, date_object, units = "days"))
print(paste("Difference in days:", date_diff))[1] "Difference in days: 808"
# Format a Date object to a specific string format
formatted_date <- format(current_date, "%B %d, %Y")
print(formatted_date)[1] "December 17, 2025"
# Format a POSIXct object to a specific string format
formatted_datetime <- format(current_datetime, "%Y-%m-%d %H:%M:%S")
print(formatted_datetime)[1] "2025-12-17 15:42:18"
These functions allow you to create, manipulate, and format date and time data in R. For more advanced date and time handling, you might consider using the lubridate package, which provides a more user-friendly interface for working with dates and times.
A practical, CDISC‑friendly guide to working with dates/times and implementing partial date imputation in R using base R and lubridate.
Different systems use different origins for numeric dates: - Excel: 1900‑01‑01 (beware 1900 leap‑year bug in older Excels) - SAS: 1960‑01‑01 - R: 1970‑01‑01
num_dates <- c(0, 159, 464, 15674)
as.Date(num_dates, origin = "1960-01-01") # SAS origin[1] "1960-01-01" "1960-06-08" "1961-04-09" "2002-11-30"
as.Date(num_dates, origin = "1970-01-01") # R origin[1] "1970-01-01" "1970-06-09" "1971-04-10" "2012-11-30"
as.Date(num_dates, origin = "1900-01-01") # Excel origin[1] "1900-01-01" "1900-06-09" "1901-04-10" "1942-12-01"
x1 <- c("01/05/1965", "08/16/1975")
as.Date(x1, "%m/%d/%Y")
x2 <- c("05-01-1965", "16-08-1975")
as.Date(x2, "%d-%m-%Y")
x3 <- c("01MAY1965", "16AUG1975")
as.Date(x3, "%d%b%Y")
# Unknown format? Try multiple patterns
x4 <- c("01MAY1965MORNING", "16AUGUST1975")
as.Date(x4, tryFormats = c("%d%b%Y", "%d%B%Y"))x <- c("2015-10-19T10:15", "2018-12-19T23:05")
# Keep the literal 'T' in the format string
as.POSIXct(x, format = "%Y-%m-%dT%H:%M", tz = "UTC")
# Extract parts
xt <- as.POSIXlt(x, format = "%Y-%m-%dT%H:%M")
format(xt, "%Y-%m-%d") # date part
format(xt, "%H:%M") # time partInstall & load: install.packages("lubridate"); library(lubridate)
ymd(), mdy(), dmy() for datesymd_hms(), ymd_hm(), ymd_h() for date‑timesymd("20210529")
dmy("29/05/2021")
ymd_hm("2019/01/19T17:15") # delimiter agnosticx <- ymd_hms("2009-08-03 12:01:59")
year(x); month(x); mday(x); wday(x); hour(x); minute(x); second(x)
floor_date(x, "month") # 2009-08-01
ceiling_date(x, "month") # 2009-09-01
round_date(x, "month") # 2009-08-01
rollback(x) # last day of previous monthddays(2)months(1)interval(start, end)start <- ymd("2015-10-19")
end <- ymd("2018-12-19")
int <- interval(start, end)
as.duration(int) # exact seconds
as.period(int) # years/months/daysImputation rules vary by study; always follow the SAP. A common pattern:
AESTDTC)YYYY-MM-DD → use as isYYYY-MM → impute day = 01 (first of month)YYYY → impute month = 01, day = 01 (01-Jan)AEENDTC)YYYY-MM-DD → use as isYYYY-MM → impute last day of that monthYYYY → impute 31-DecAdditionally, bound AEENDT by death / last alive date (when present):
CENSOR = coalesce(DTHDT, LASTALVDT)AEENDT = min(imputed_end, CENSOR)Add flags:
AESTDTF, AEENDTF:
"D" → day imputed"M,D" → month & day imputed"C" if we had to censor the end date at DTHDT / LASTALVDT.library(dplyr, warn.conflicts = FALSE)
library(stringr, warn.conflicts = FALSE)
library(lubridate, warn.conflicts = FALSE)
df <- data.frame(
USUBJID = sprintf("01-001-%03d", 1:6),
AESTDTC = c("2015-10-19", "2015-10", "2015", "2019-02", "2020-02", "2018-12-31"),
AEENDTC = c("2018-12-19", "2018-10", "2017-05-29", "2019-02", "2020-02", "2020"),
RANDDT = ymd(c("2015-10-01", "2015-10-01", "2015-01-10", "2019-01-15", "2020-02-10", "2018-12-01")),
DTHDT = ymd(c(NA, NA, NA, NA, "2020-08-03", NA)),
LASTALVDT = ymd(c(NA, NA, NA, NA, NA, "2020-12-15"))
)
df_imp <- df |>
mutate(
# Length of partial date strings
AESTDTC_LEN = str_length(AESTDTC),
AEENDTC_LEN = str_length(AEENDTC),
## --- AE start date imputation (minimum: first possible date) ---
AESTDT = case_when(
is.na(AESTDTC) ~ as.Date(NA),
AESTDTC_LEN == 10 ~ ymd(AESTDTC, quiet = TRUE),
AESTDTC_LEN == 7 ~ ymd(str_c(AESTDTC, "-01"), quiet = TRUE),
AESTDTC_LEN == 4 ~ ymd(str_c(AESTDTC, "-01-01"), quiet = TRUE),
TRUE ~ as.Date(NA)
),
AESTDTF = case_when(
is.na(AESTDTC) ~ NA_character_,
AESTDTC_LEN == 10 ~ "",
AESTDTC_LEN == 7 ~ "D",
AESTDTC_LEN == 4 ~ "M,D",
TRUE ~ "UNK"
),
## --- AE end date imputation (maximum: latest possible date) ---
AEENDT_RAW = case_when(
is.na(AEENDTC) ~ as.Date(NA),
AEENDTC_LEN == 10 ~ ymd(AEENDTC, quiet = TRUE),
AEENDTC_LEN == 7 ~ ymd(str_c(AEENDTC, "-01"), quiet = TRUE),
AEENDTC_LEN == 4 ~ ymd(str_c(AEENDTC, "-12-31"), quiet = TRUE),
TRUE ~ as.Date(NA)
),
AEENDTF_RAW = case_when(
is.na(AEENDTC) ~ NA_character_,
AEENDTC_LEN == 10 ~ "",
AEENDTC_LEN == 7 ~ "D",
AEENDTC_LEN == 4 ~ "M,D",
TRUE ~ "UNK"
)
) %>%
mutate(
AEENDT_RAW = if_else(
AEENDTC_LEN == 7 & !is.na(AEENDT_RAW),
ceiling_date(AEENDT_RAW, "month") - days(1),
AEENDT_RAW
),
CENSOR = coalesce(DTHDT, LASTALVDT),
AEENDT = case_when(
!is.na(CENSOR) & !is.na(AEENDT_RAW) & AEENDT_RAW > CENSOR ~ CENSOR,
TRUE ~ AEENDT_RAW
),
AEENDTF = case_when(
is.na(AEENDT) ~ AEENDTF_RAW,
!is.na(CENSOR) & !is.na(AEENDT_RAW) & AEENDT_RAW > CENSOR & AEENDTF_RAW == "" ~ "C",
!is.na(CENSOR) & !is.na(AEENDT_RAW) & AEENDT_RAW > CENSOR & AEENDTF_RAW != "" ~ str_c(AEENDTF_RAW, ",C"),
TRUE ~ AEENDTF_RAW
)
)AEDUR <- as.integer(out$AEENDT - out$ASTDT)
ymd(), mdy(), dmy(), yq()ymd_hms(), ymd_hm(), ymd_h()hms(), hm(), ms()year(), month(), mday(), wday(), hour(), minute(), second(), date()floor_date(), ceiling_date(), round_date(), rollback()