0

I've found a million posts about converting from GMT to local time, but not one that first checks if the date-time is in GMT, and then converting it to its representative local time. As folks are suggesting a duplicate, I am looking to convert from GMT to local time, but more importantly, specifically want to check whether or not a conversion needs to be made. I'm using lubridate, dplyr, zoo, and stringr.

Take this example data (my data is in a larger data frame of nums and POSXIct formats):

for store 1000, the df dataframe has the column Time:

Time<-c("2017-02-02 06:05:00 GMT", "2017-02-04 00:06:10 GMT", "2017-02-05 00:06:15 GMT")
df<-as.data.frame(Time)

time_zone_info is as follows:

x<-1000
timezone<-"US/Eastern"
time_zone_info<-cbind(x,timezone)
time_zone_info<-as.data.frame(time_zone_info)

my current attempt is below:

correct_to_local_time<-function(df){     
    if("GMT" %in% df$Time){
        df$Time<-as.POSIXct(df$Time, tz ="GMT",usetz=TRUE) 
        df$Time<-with_tz(df$Time,tz=time_zone_info$timezone)
        }
    else{
           as.POSIXct(df$Time)}
}
correct_to_local_time(df)

which gives me the following which is computationally incorrect (it should be 1:05:00EDT, 01:10:00EDT, etc., but it did change the GMT to EDT, so I'm quite confused...):

Time
2017-02-02 06:05:00 EDT
2017-02-02 06:10:00 EDT
2017-02-02 06:10:00 EDT

Ideally I'd like something like:

 Local Time        
2017-02-02 01:05:00 EDT
2017-02-04 01:10:00 EDT
2017-02-05 01:15:00 EDT

Once a conversion from GMT to EDT or MST is made, are daylight savings accounted for?

Thanks in advance ya'll

longlivebrew
  • 301
  • 3
  • 16
  • doesn't that create a list though? I'd want to maintain my data frame format - which is a bunch of vectors – longlivebrew Nov 17 '17 at 22:50
  • Possible duplicate of [Convert date string that contains time zone to POSIXct in R](https://stackoverflow.com/questions/33286315/convert-date-string-that-contains-time-zone-to-posixct-in-r) – Mako212 Nov 17 '17 at 22:56
  • Thanks @Mako212 but that doesn't really seem relevant. See, I am converting from GMT and need to include a checking function. – longlivebrew Nov 17 '17 at 22:57
  • Do you have store ID in both data frames? If you do, you should just be able to join based on store ID, then construct your time object from the time/time zone columns. – Mako212 Nov 17 '17 at 23:01
  • No, I don't actually. I iterate over each storeID individually, and therefore generate the time_zone_info for each iteration. – longlivebrew Nov 17 '17 at 23:03

2 Answers2

1

The package lubridate probably can help you.

 require(lubridate)

dfTime<-c("2017-02-02 06:05:00 GMT", 
          "2017-02-04 00:06:00 New_York",
          "2017-02-05 00:06:00 US/Mountain")
dfStore <-c(1000, 2000, 3000)
dftimeZone<-c("US/Eastern", "US/Eastern", "US/Mountain")

time_zone_info <- data.frame(Store = dfStore,
                             Time = dfTime,
                             Timezone = dftimeZone,
                             stringsAsFactors = FALSE)

First, check that all your time zones are within your system's recognized list of time zones. (I wasn't sure.)

all(time_zone_info$Timezone %in% OlsonNames())

Ok, good.

Next, convert your character string of times into POSIXct. I like the lubridate function "ymd_hms" here because it's so flexible. You can also set what the time zone was for that, too, using the argument "tz". This returns the time IN YOUR CURRENT TIME ZONE. For example, I'm in Seattle, so this returns the equivalent time in PST for me. I'm pretty sure lubridate accounts for daylight savings time. (I'm having difficulty getting this to sapply and thus opting for a "for" loop. I'm sure someone else could make this happen without that, though.)

time_zone_info$Time2 <- as.POSIXct(NA)
for(i in 1:nrow(time_zone_info)){
      time_zone_info$Time2[i] <- ymd_hms(time_zone_info$Time[i], 
                                         tz = time_zone_info$Timezone[i])
}

Everything in time_zone_info$Time2 will be in the same time zone, and I believe that's by design for making easy comparisons. It's hard to get R to make multiple time zones in the same column of a data.frame.

shirewoman2
  • 1,842
  • 4
  • 19
  • 31
  • LauraS thank you! I actually just updated it for clarity and changed my method so it's not throwing an error, but still not working computationally - thank you for the help by way :) It looks like it is changing the timezone from GMT to EDT (yay!) but not changing the time itself, which I think is incorrect. – longlivebrew Nov 18 '17 at 02:52
  • Hmm... After your edit, I'm not actually clear on how your example function is working because you haven't defined what `df` is. Instead of `Time<-as.data.frame(Time)`, did you mean to write `df<-as.data.frame(Time)`? Also, it looks as though your function is calling on one data.frame (df) for the Time info and another data.frame (time_zone_info) for the time zone info. That's really dangerous because you haven't done anything to guarantee that the rows are in the same order. This is basically coding by index rather than name and generally ill advised. – shirewoman2 Nov 18 '17 at 18:26
  • I've updated the `df` function so it should be correct - nice catch. There will only be one row in `time_zone_info` per iteration so `time_zone_info$timezone` should always be correct. I've made some progress (using the format function to display the time in a different time zone), but my check for 'GMT' substring isn't working quite right ~(Should be updated now) – longlivebrew Nov 18 '17 at 18:31
1

Let's start by making a data.frame that's a little closer to what you're actually working with and also a little easier to work with than a one column data.frame that you submit one row of to your function.

df <- data.frame(
      Store = c(1000, 2000, 3000),
      Time = c("2017-02-02 06:05:00 GMT",
               "2017-02-04 00:06:10 GMT",
               "2017-02-05 00:06:15 GMT"),
      stringsAsFactors = FALSE)

Similarly, let's write the other data.frame more concisely and make the classes character strings rather than factors just because factors can trip you up. (This is more a preference than a requirement.)

time_zone_info <- data.frame(
      Store = 1000,
      timezone = "US/Eastern",
      stringsAsFactors = FALSE)

One problem with your function is that you're using the wrong syntax to check whether the characters "GMT" are in your character string. You don't want %in%; you want something like stringr's handy str_detect function.

Also, rather than inputting a data.frame and outputting a data.frame, which can be tricky as you've currently applied it, let's design the function so that the input is a single string and the output is a single string, which is easier to work with.

require(stringr)
require(lubridate)

correct_to_local_time<-function(InputTime){     
      if(str_detect(InputTime, "GMT")){
            GMTTime <- as.POSIXct(InputTime, tz = "GMT", usetz = TRUE) 
            OutputTime <- with_tz(GMTTime, tz = time_zone_info$timezone)
      } else {
            OutputTime <- as.POSIXct(InputTime)}

      return(OutputTime)
}
correct_to_local_time(df$Time[1])

If I were you, I'd also rigorously check that the else portion of your function is returning what you want; I could see that not working with non-standardized time zone info.

shirewoman2
  • 1,842
  • 4
  • 19
  • 31
  • the data that I'm getting is from one of our software engineers, so I'm hoping it will be fairly consistent. I was planning on adding filters to convert from US/Eastern, US/Central etc, since some of our data is in that format. But IT WORKS! I didn't think about scoping and creating `GMTTime` and `OutputTime` – longlivebrew Nov 18 '17 at 19:47
  • how would you recommend accounting for non-standard times though? do you have an example post on here? :P thank you again @LauraS - I super appreciate it :) – longlivebrew Nov 18 '17 at 19:48
  • You're welcome! I've had a LOT of help on SO, so I like to pay if forward when I can! As for accounting for non-standard times, no, I don't have an example post or anything. I'm thinking more like cases where someone has misspelled the time zone or something like that, and as the function is currently written, I think it might just convert it to UTC and not necessarily return the time you're expecting. – shirewoman2 Nov 20 '17 at 00:47
  • That's a good point - maybe it would be good to parse the timezone and run it through OlsonNames like you said ~ it's bogging me down to try to figure out why my attempt (in post) didn't do the job... any thoughts? – longlivebrew Nov 20 '17 at 20:23