as.POSIXct is mishandling timestamp with leading zero in the milliseconds portion
You are using incorrect format. Try -
time_043_millis <- "20210909-20:05:10.043"
time_143_millis <- "20210909-20:05:10.143"
as.POSIXct(time_043_millis, format = "%Y%m%d-%H:%M:%OS", tz = 'UTC')
#[1] "2021-09-09 20:05:10 UTC"
as.POSIXct(time_143_millis, format = "%Y%m%d-%H:%M:%OS", tz = 'UTC')
#[1] "2021-09-09 20:05:10 UTC"
Milliseconds in POSIXtc
You are missing the options(digits.secs=6)
setting.
Demo
R> data <- read.csv(text="dt,val
+ 2010-03-01 13:02:00.0010070, 0.030
+ 2010-03-01 13:02:00.0020141, 0.031
+ 2010-03-01 13:02:00.0030059, 0.035
+ 2010-03-01 13:02:00.0040130, 0.041
+ 2010-03-01 13:02:00.0050048, 0.049
+ 2010-03-01 13:02:00.0060119, 0.060")
R>
R> data[,1] <- anytime::anytime(data[, 1]) ## or as.POSIXct or ...
R>
R> options(digits.secs=6)
R> data
dt val
1 2010-03-01 13:02:00.0010070 0.030
2 2010-03-01 13:02:00.0020141 0.031
3 2010-03-01 13:02:00.0030059 0.035
4 2010-03-01 13:02:00.0040130 0.041
5 2010-03-01 13:02:00.0050048 0.049
6 2010-03-01 13:02:00.0060119 0.060
R>
R> class(data[,1])
[1] "POSIXct" "POSIXt"
R>
The zoo package has a few fine vignettes showing to slice, dice and aggregate data based on timestamps.
as.POSIXct/as.POSIXlt doesn't like .61 milliseconds
If you change the format for the time part to be %H%M%OS
instead of %H%M%S.%OS
, it seems to parse correctly. You may have to adjust your options
so see this:
as.POSIXlt(vec, tz = "EST", format = "%Y%m%d.%H%M%OS")
#[1] "2015-01-01 01:01:01 EST" "2015-01-01 01:01:01 EST"
#[3] "2015-01-01 01:01:01 EST"
options(digits.secs = 2)
as.POSIXlt(vec, tz = "EST", format = "%Y%m%d.%H%M%OS")
# [1] "2015-01-01 01:01:01.60 EST" "2015-01-01 01:01:01.61 EST"
# [3] "2015-01-01 01:01:01.62 EST"
Converting unix seconds in milliseconds to POSIXct/POSIXlt
The help page actually hints at a difference:
Value:
‘as.POSIXct’ and ‘as.POSIXlt’ return an object of the appropriate
class. If ‘tz’ was specified, ‘as.POSIXlt’ will give an
appropriate ‘"tzone"’ attribute.
This stuff is finicky -- I think there is an implicit TZ conversion happening for as.POSIXct
. Consider that
R> print(as.numeric(as.POSIXct(as.POSIXlt(1268736919,
origin="1970-01-01"))), digits=10)
[1] 1268736919
R> print(as.numeric(as.POSIXct(1268736919, origin="1970-01-01")), digits=10)
[1] 1268758519
the second one (using as.POSIXct
) does not return the original input. Unfortunately, Brian D. Ripley seems to be the only human having all the details here.
Lastly, you can't do it without the origin. But you could define wrappers that use the epoch as origin (as here) or use 2000-01-01 or ... Just keep it consistent.
r - converting POSIXct to milliseconds
Summary answer
The short answer to this is to do with how numbers are printed in scientific format
To see this we can set options(scipen=1000)
and we get the result as expected.
options(scipen=1000);
nchar(d*1000)
# [1] 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13
longer answer
The background to this question came from trying to query a mongodb database using a date range in library(mongolite)
For example, this question and this issue show that to query a date it needs to be converted to numberLong
for the query to work correctly.
To illistrate this, and the issue I was having, consider this data and subsequent queries
library(mongolite)
df <- data.frame(date = as.POSIXct(c("2015-12-19","2015-12-20","2015-12-21")),
val = c(1,2,3))
mongo <- mongo(collection = "test", db = "test", url = "mongodb://localhost")
## insert data into test database
mongo$insert(df)
## querying the 19th December 2015 works as expected, because d is formatted with 13 digits
d <- as.integer(as.POSIXct("2015-12-19")) * 1000
q <- paste0('{"date" : {"$date" : { "$numberLong" : "', d,'" } } }')
mongo$find(query = q)
#
# Imported 1 records. Simplifying into dataframe...
# date val
# 1 2015-12-19 1
## the 20th December 2015 errors, as d is formatted with < 13 digits
d <- as.integer(as.POSIXct(("2015-12-20"))) * 1000
q <- paste0('{"date" : {"$date" : { "$numberLong" : "', d,'" } } }')
mongo$find(query = q)
#
# Error: Invalid input string 1.45053e+12, looking for 11
## the 21st December 2015 works as expected because d is formatted with 13 digits.
d <- as.integer(as.POSIXct("2015-12-21")) * 1000
q <- paste0('{"date" : {"$date" : { "$numberLong" : "', d,'" } } }')
mongo$find(query = q)
#
# Imported 1 records. Simplifying into dataframe...
# date val
# 1 2015-12-21 3
## cleanup
rm(mongo); gc()
So to resolve this I either need to set options(scipen=1000)
, or right-pad my d
with zeroes when it goes into the query.
Related Topics
Split One Row into Multiple Rows
R Ggplot2: Stat_Count() Must Not Be Used with a Y Aesthetic Error in Bar Graph
R Scatter Plot: Symbol Color Represents Number of Overlapping Points
Ggplot2 Multiple Scales/Legends Per Aesthetic, Revisited
Replace Logical Values (True/False) with Numeric (1/0)
Create a Time Interval of 15 Minutes from Minutely Data in R
Reason Behind Speed of Fread in Data.Table Package in R
How to Jitter/Dodge Geom_Segments So They Remain Parallel
Insert a Blank Row After Each Group of Data
How to Specify a Dynamic Position for the Start of Substring
Change Values in Multiple Columns of a Dataframe Using a Lookup Table
R Plotting Confidence Bands with Ggplot
How to Extract Just the Number from a Named Number (Without the Name)
Stop an R Program Without Error
Select Row with Most Recent Date by Group
How to Use Tidyr::Separate When the Number of Needed Variables Is Unknown