views:

480

answers:

3

Hello All,

I need to convert time between timezones in C (on linux, so anything specific would do too).

I know my current time, local and UTC, I have the offset of the target time. I am trying to use mktime, gmtime, localtime and similar set of functions but still can't figure it out.

Thanks in advance.

+1  A: 

In all likelyhood, your operating system provides some support for this.

In unix derived OSs you might want to look at the man pages for asctime, asctime_r, ctime, ctime_r, difftime, gmtime, gmtime_r, localtime, localtime_r, mktime, timegm.

dmckee
The Posix APIs you mention don't really offer an obvious way for doing what the question asks. The questions already mentions some of these functions by name, so mentioning them again and pointing at the man pages, basically, is not likely to be a useful answer.
Ori Pessach
+2  A: 

You can use gmtime() and the tm structure to directly set this, provided you know the offsets.

If you know your local time and UTC, you know your local offset. Provided you also know the target offset, it's just a matter of setting tm_hour appropriate (and potentially flipping the day, too, if you go <0 or >23).

For some sample code, see this gmtime reference page. It shows offsetting based off time zone offsets.


Edit:

In response to the comments - you can also let mktime handle the shifting for you, which allows you to simplify this by converting back to a time_t. You can use something like:

time_t currentTime;
tm * ptm;
time ( &currentTime );
ptm = gmtime ( &rawtime );
ptm->tm_hour += hours_to_shift;
ptm->tm_minutes += minutes_to_shift; // Handle .5 hr timezones this way

time_t shiftedTime = mktime( ptm );
// If you want to go back to a tm structure:

tm * pShiftedTm = gmtime( &shiftedTime );
Reed Copsey
FWIW, not only tm_hour - e.g. India Standard Time (IST) is UTC+5.5
Andrew Y
Yes, true. You'd have to potentially manipulate nearly any of the fields, but the concept is sound.
Reed Copsey
Actually, I think there is a way to avoid doing too much arithmetics manually... since the comments do not allow the code, I'll post it as another answer.
Andrew Y
hmm .. I read the reference pages on mktime which says that if I modify the tm struct I can use mktime to normalize the time. So that will take care of normalizing the dates and all otherwise it could get complicated. I may then want to convert this time back to tm struct (I don;t know if I should use localtime, or gmtime) so that I could use functions like strftime etc to get formatted time values.
verma
Good point, I'll edit to update.
Reed Copsey
+3  A: 

As comments do not allow posting the code, posting as a separate answer.. If you know "local" time and "UTC" time, you can calculate the offset of the "other" time from your "local" time. Then you convert the struct tm into calendar time, add the desired number of seconds (being the offset of the target time), and convert it back to struct tm:

(edited to account for another scenario to use mktime's normalization)

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>

int main(int argc, char *argv) {
  struct timeval tv_utc;
  struct tm *local_tm, *other_tm;

  /* 'synthetic' time_t to convert to struct tm for the other time */
  time_t other_t_synt;
  /* Other time is 1 hour ahead of local time */
  int other_local_delta = 1*3600; 


  /* the below two lines are just to set local_tm to something */
  gettimeofday(&tv_utc, NULL);
  local_tm = localtime(&tv_utc.tv_sec);

  printf("Local time: %s", asctime(local_tm));

  #ifdef DO_NOT_WRITE_TO_LOCAL_TM
  other_t_synt = mktime(local_tm) + other_local_delta;
  #else
  local_tm->tm_sec += other_local_delta;
  /* mktime will normalize the seconds to a correct calendar date */
  other_t_synt = mktime(local_tm);
  #endif

  other_tm = localtime(&other_t_synt);

  printf("Other time: %s", asctime(other_tm));

  exit(0);
}
Andrew Y
This seems to be working well. I am going to use local_tm->tm_sec += 3600; other_t = mktime(local_tm); instead and let mktime do all the normalization stuff for me.
verma
Yes, that should work as well - I was trying to be polite and avoid writing on someone else's memory, since the memory pointed by local_tm in this example does not belong to my code.
Andrew Y
Thats a good point.
verma
Anyway, autonormalization is a good thing, I've edited the example with your usage as well. I thought that the 'epoch' time other_t in this scenario will be synthetic once you add the delta between other time and your time, so I also renamed it accordingly.
Andrew Y
looks great! thanks!
verma
This assumes we know the delta between current and other. Or UCT and other would work too. But that delta isn't necessarily a constant over the year (DST starting/ending in various countries or even changes to localtime conversion rules for a particular zone). Is there a *general* way to convert from zone A to zone B for an arbitrary Unix epoch value?
John M
I think you can set the environment variable "TZ" and then use tzset to "pretend" your local time is either in Zone A or Zone B. That's the only more or less "generic" approach that I would think of.
Andrew Y
@John M: I've decided to post your request as a separate question, along with some sample code - maybe there are better ways to do it.http://stackoverflow.com/questions/1217566/general-way-to-manipulate-the-times-between-timezones-in-c
Andrew Y