8 min read

Date & Time in R - Date & Time Classes


This is the second tutorial in the series Handling Date & Time in R. In this tutorial, we will learn about date and time classes in R.


Below are the links to all the resources related to this tutorial:

new courses ad

Date & Time Classes

In this section, we will look at two things. First, how to create date/time data in R, and second, how to convert other data types to date/time. Let us begin by creating the release date of R 3.6.2.

release_date <- 2019-12-12
## [1] 1995

Okay! Why do we see 1995 when we call the date? What is happening here? Let us quickly check the data type of release_date.

## [1] "numeric"

The data type is numeric i.e. R has subtracted 12 twice from 2019 to return 1995. Clearly, the above method is not the right way to store date/time. Let us see if we can get some hints from the built-in R functions we used in the previous section. If you observe the output, all of them returned date/time wrapped in quotes. Hmmm… let us wrap our date in quotes and see what happens.

release_date <- "2019-12-12"
## [1] "2019-12-12"

Alright, now R does not do any arithmetic and returns the date as we specified. Great! Is this the right format to store date/time then? No. Why? What is the problem if date/time is saved as character/string? The problem is the nature or type of operations done on date/time are different when compared to string/character, number or logical values.

  • how do we add/subtract dates?
  • how do we extract components such as year, month, day etc.

To answer the above questions, we will first check the data type of Sys.Date() and now().

## [1] "Date"
## [1] "POSIXct" "POSIXt"
## [1] "character"

As you can see from the above output, there are 3 different classes for storing date/time in R

  • Date
  • POSIXct
  • POSIXlt

Let us explore each of the above classes one by one.



The Date class represents calendar dates. Let us go back to Sys.Date(). If you check the class of Sys.Date(), it is Date. Internally, this date is a number i.e. an integer. The unclass() function will show how dates are stored internally.

## [1] "2020-06-26"
## [1] 18439

What does this integer represent? Why has R stored the date as an integer? In R, dates are represented as the number of days since 1970-01-01. All the dates in R are internally stored in this way. Before we explore this concept further, let us learn to create Date objects in R. We will continue to use the release date of R 3.6.2, 2019-12-12.

Until now, we have stored the above date as character/string but now we will use as.Date() to save it as a Date object. as.Date() is the easiest and simplest way to create dates in R.

release_date <- as.Date("2019-12-12")
## [1] "2019-12-12"

The as_date() function from the lubridate package is similar to as.Date().

release_date <- lubridate::as_date("2019-12-12")
## [1] "2019-12-12"

If you look at the difference between release_date and 1970-01-01, it will be the same as unclass(release_date).

release_date - as.Date("1970-01-01")
## Time difference of 18242 days
## [1] 18242

Let us come back to 1970-01-01 i.e. the origin for dates in R.

## [1] "1970-01-01 UTC"

From the previous examples, we know that dates are internally stored as number of days since 1970-01-01. How about dates older than the origin? How are they stored? Let us look at that briefly.

## [1] -2318

Dates older than the origin are stored as negative integers. For those who are not aware, Martin Luther King, Jr. delivered his famous I Have a Dream speech on 1963-08-28. Let us move on and learn how to convert numbers into dates.

Convert Numeric

The as.Date() function can be used to convert any of the following to a Date object

  • character/string
  • number
  • factor (categorical/qualitative)

We have explored how to convert strings to date. How about converting numbers to date? Sure, we can create date from numbers by specifying the origin and number of days since it.

as.Date(18242, origin = "1970-01-01")
## [1] "2019-12-12"

The origin can be changed to another date (while changing the number as well.)

as.Date(7285, origin = "2000-01-01")
## [1] "2019-12-12"

ISO 8601

If you have carefully observed, the format in which we have been specifying the dates as well as of those returned by functions such as Sys.Date() or Sys.time() is the same i.e. YYYY-MM-DD. It includes

  • the year including the century
  • the month
  • the date

The month and date separated by -. This default format used in R is the ISO 8601 standard for date/time. ISO 8601 is the internationally accepted way to represent dates and times and uses the 24 hour clock system. Let us create the release date using another function ISOdate().

ISOdate(year  = 2019,
        month = 12,
        day   = 12,
        hour  = 8,
        min   = 5, 
        sec   = 3,
        tz    = "UTC")
## [1] "2019-12-12 08:05:03 UTC"

We will look at all the different weird ways in which date/time are specified in the real world in the [Date & Time Formats] section. For the time being, let us continue exploring date/time classes in R. The next class we are going to look at is POSIXct/POSIXlt.


You might be wondering what is this POSIX thing? POSIX stands for Portable Operating System Interface. It is a family of standards specified for maintaining compatibility between different operating systems. Before we learn to create POSIX objects, let us look at now() from lubridate.

## [1] "POSIXct" "POSIXt"

now() returns current date/time as a POSIXct object. Let us look at its internal representation using unclass()

## [1] 1593167657
## attr(,"tzone")
## [1] ""

The output you see is the number of seconds since January 1, 1970.


POSIXct represents the number of seconds since the beginning of 1970 (UTC) and ct stands for calendar time. To store date/time as POSIXct objects, use as.POSIXct(). Let us now store the release date of R 3.6.2 as POSIXct as shown below

release_date <- as.POSIXct("2019-12-12 08:05:03")
## [1] "POSIXct" "POSIXt"
## [1] 1576118103
## attr(,"tzone")
## [1] ""


POSIXlt represents the following information in a list

  • seconds
  • minutes
  • hour
  • day of the month
  • month
  • year
  • day of week
  • day of year
  • daylight saving time flag
  • time zone
  • offset in seconds from GMT

The lt in POSIXlt stands for local time. Use as.POSIXlt() to store date/time as POSIXlt objects. Let us store the release date as a POSIXlt object as shown below

release_date <- as.POSIXlt("2019-12-12 08:05:03")
## [1] "2019-12-12 08:05:03 IST"

As we said earlier, POSIXlt stores date/time components in a list and these can be extracted. Let us look at the date/time components returned by POSIXlt using unclass().

release_date <- as.POSIXlt("2019-12-12 08:05:03")
## $sec
## [1] 3
## $min
## [1] 5
## $hour
## [1] 8
## $mday
## [1] 12
## $mon
## [1] 11
## $year
## [1] 119
## $wday
## [1] 4
## $yday
## [1] 345
## $isdst
## [1] 0
## $zone
## [1] "IST"
## $gmtoff
## [1] NA

Use unlist() if you want the components returned as a vector.

release_date <- as.POSIXlt("2019-12-12 08:05:03")
##    sec    min   hour   mday    mon   year   wday   yday  isdst   zone gmtoff 
##    "3"    "5"    "8"   "12"   "11"  "119"    "4"  "345"    "0"  "IST"     NA

To extract specific components, use $.

release_date <- as.POSIXlt("2019-12-12 08:05:03")
## [1] 8
## [1] 11
## [1] "IST"

Now, let us look at the components returned by POSIXlt. Some of them are intuitive

Component Description
sec Second
min Minute
hour Hour of the day
mon Month of the year (0-11
zone Timezone
wday Day of week
mday Day of month
year Years since 1900
yday Day of year
isdst Daylight saving flag
gmtoff Offset is seconds from GMT

Great! We will end this section with a few tips/suggestions on when to use Date or POSIXct/POSIXlt.

  • use Date when there is no time component
  • use POSIX when dealing with time and timezones
  • use POSIXlt when you want to access/extract the different components

Your Turn

R 1.0.0 was released on 2000-02-29 08:55:23 UTC. Save it as

  • Date using character
  • Date using origin and number
  • POSIXct
  • POSIXlt and extract
    • month day
    • day of year
    • month
    • zone
  • ISODate

*As the reader of this blog, you are our most important critic and commentator. We value your opinion and want to know what we are doing right, what we could do better, what areas you would like to see us publish in, and any other words of wisdom you are willing to pass our way.

We welcome your comments. You can email to let us know what you did or did not like about our blog as well as what we can do to make our post better.*