Question

How do I convert milliseconds to minutes and seconds?

I want to convert milliseconds values in my data to minutes and seconds (in the format MM:SS e.g. a value of 1772094 should become 29:32).

The toy data looks like this:

df <- data.frame(
  ID = c("ID1", "ID2", "ID3"),
  duration = c(1723456, 1834537, 1945678)
)

The output I want is:

id duration
ID1 28:43
ID2 30:35
ID3 32:26

I have gotten close but not perfect with the below code.

library(dplyr)
library(lubridate)
library(hms)

df2 <- df %>%
  mutate(duration_ms = seconds_to_period(duration/1000)) %>% 
  mutate(duration_ms = hms::hms(duration_ms))

I divide my millisecond values by a thousand to turn them into seconds so they can be used by the seconds_to_period() function in lubridate and then format them with hms.

But it's currently HH:MM:SS with the seconds having decimal places (not rounded).

How do I turn millisecond values into minutes and seconds values in the format MM:SS in R?

 3  101  3
1 Jan 1970

Solution

 2

You can simply round() when you divide duration by 1000:

df %>%
    mutate(duration_ms = seconds_to_period(round(duration / 1000))) %>%
    mutate(duration_ms = hms::hms(duration_ms))

#    ID duration duration_ms
# 1 ID1  1723456    00:28:43
# 2 ID2  1834537    00:30:35
# 3 ID3  1945678    00:32:26

Note that rounding means that your second example, which is in fact 00:30:34.537 seconds, becomes 00:30:35. If you prefer to always round down you can use floor():

df %>%
    mutate(duration_ms = seconds_to_period(floor(duration / 1000))) %>%
    mutate(duration_ms = hms::hms(duration_ms))

#    ID duration duration_ms
# 1 ID1  1723456    00:28:43
# 2 ID2  1834537    00:30:34
# 3 ID3  1945678    00:32:25
2024-07-21
SamR

Solution

 2

I would use format():

 df = data.frame(ID = c("ID1", "ID2", "ID3"), duration = c(1723456, 1834537, 1945678))
 f = \(d) format(as.POSIXct(d/1e3), "%M:%S")
 f(df$duration)
#> [1] "28:43" "30:34" "32:25"
2024-07-21
Friede

Solution

 2

Write a function to do it:

milli_to_ms <- function(milliseconds){
    s = milliseconds / 1000 
    hm = paste0(s %/% 60,":", round(s %% 60))
    hm    
}

which you can then test:

> milli_to_ms(1000)
[1] "0:1"
> milli_to_ms(6000)
[1] "0:6"
> milli_to_ms(60000)
[1] "1:0"

is that right? Or do you need leading zeroes? Rewrite, this time using sprintf to control field size and leading zeroes:

milli_to_ms <- function(milliseconds){
    s = milliseconds / 1000 
    return(sprintf("%02d:%02d",s %/% 60, round(s%%60)))
}

and test again:

> milli_to_ms(0)
[1] "00:00"
> milli_to_ms(1)
[1] "00:00"
> milli_to_ms(1000)
[1] "00:01"
> milli_to_ms(10000)
[1] "00:10"
> milli_to_ms(100000)
[1] "01:40"

Look good now? Then apply to your problem:

> df$duration = milli_to_ms(df$duration)
> df
   ID duration
1 ID1    28:43
2 ID2    30:35
3 ID3    32:26
> 

You could do it with a mutate but there's no real value in that when you can do it this way quicker.

2024-07-21
Spacedman