6

1) R version 3.4.4 (2018-03-15)

my.timedate <- as.POSIXlt('2016-01-01 16:00:00')  
# print(attributes(my.timedate))  
print(my.timedate[['hour']])  

[1] 16

2) R version 3.5.0 (2018-04-23)

my.timedate <- as.POSIXlt('2016-01-01 16:00:00')  
# print(attributes(my.timedate))  
print(my.timedate[['hour']]) 

Error in FUN(X[[i]], ...) : subscript out of bounds

Henrik
  • 65,555
  • 14
  • 143
  • 159

3 Answers3

9

I think that is a known change in R 3.5.0 where the list elements of a POSIXlt need to be unpackaged explicitly. Using R 3.5.0:

edd@rob:~$ docker run --rm -ti r-base:3.5.0 \
               R -q -e 'print(unclass(as.POSIXlt("2016-01-01 16:00:00")[["hour"]])'
> print(unclass(as.POSIXlt("2016-01-01 16:00:00"))[["hour"]])
[1] 16
> 
> 
edd@rob:~$ 

whereas with R 3.4.* one does not need the unclass() as you showed:

edd@rob:~$ docker run --rm -ti r-base:3.4.3 \
               R -q -e 'print(as.POSIXlt("2016-01-01 16:00:00")[["hour"]])'
> print(as.POSIXlt("2016-01-01 16:00:00")[["hour"]])
[1] 16
> 
> 
edd@rob:~$ 

I don't find a corresponding NEWS file entry though so not entirely sure if it is on purpose...

Edit: As others have noted, the corresponding NEWS entry is the somewhat opaque

* Single components of "POSIXlt" objects can now be extracted and
  replaced via [ indexing with 2 indices.
Dirk Eddelbuettel
  • 360,940
  • 56
  • 644
  • 725
  • The original example can be found in "Processing and Analyzing Financial Data with R" by Marcelo S. Perlin, 2017 (pages 108-109). I simply replicated it (with errors) in R 3.5.0. This is a simple example code. What is the best way to ensure that a complex code written in one version of R does not suddenly begin to return errors in a newer version, due to changes that maybe opaque? – George Rwaga Jun 17 '18 at 15:44
  • That is a very valid question, and an important. Base R is usually exemplary about ensuring compatibility but there are rare times when a change has to be made. Note though that you can also use accessors such as `hour() ` in `data.table`. They offer a layer of indirection. – Dirk Eddelbuettel Jun 17 '18 at 15:52
5

See ?DateTimeClasses (same as ?as.POSIXlt):

As from R 3.5.0, one can extract and replace single components via [ indexing with two indices

See also similar description in R NEWS CHANGES IN R 3.5.0.

Thus:

my.timedate[1, "hour"]
# [1] 16

# or leave the i index empty to select a component
# from all date-times in a vector 
as.POSIXlt(c('2016-01-01 16:00:00', '2016-01-01 17:00:00'))[ , "hour"]
# [1] 16 17

See also Examples in the help text.

Henrik
  • 65,555
  • 14
  • 143
  • 159
5

From ?POSIXlt:

As from R 3.5.0, one can extract and replace single components via [ indexing with two indices (see the examples).

The example is a little opaque, but shows the idea:

leapS[1 : 5, "year"]

If you look at the source, though, you can see what's happening:

`[.POSIXlt`
#> function (x, i, j, drop = TRUE) 
#> {
#>     if (missing(j)) {
#>         .POSIXlt(lapply(X = unclass(x), FUN = "[", i, drop = drop), 
#>             attr(x, "tzone"), oldClass(x))
#>     }
#>     else {
#>         unclass(x)[[j]][i]
#>     }
#> }
#> <bytecode: 0x7fbdb4d24f60>
#> <environment: namespace:base>

It is using i to subset unclass(x), where x is the POSIXlt object. So with R 3.5.0, you use [ and preface the part of the datetime you want with the index of the datetime in the vector:

my.timedate <- as.POSIXlt('2016-01-01 16:00:00')

my.timedate[1, 'hour']
#> [1] 16

as.POSIXlt(seq(my.timedate, by = 'hour', length.out = 10))[2:5, 'hour']
#> [1] 17 18 19 20

Note that $ subsetting still works as usual:

my.timedate$hour
#> [1] 16
alistaire
  • 42,459
  • 4
  • 77
  • 117