C code to get local time offset in minutes relative to UTC?

后端 未结 5 2390
不思量自难忘°
不思量自难忘° 2021-02-20 07:40

I didn\'t find a trivial way to get the time offset in minutes between the local time and the UTC time.

At first I intended to use tzset() but it doesn\'t p

相关标签:
5条回答
  • 2021-02-20 08:01

    ... to get local time offset ... relative to UTC?

    @Serge Ballesta answer is good. So I though I would test it and clean-up a few details. I would have posted as a comment but obviously too big for that. I only exercised it for my timezone, but though others may want to try on their machine and zone.

    I made to community wiki as not to garner rep. Imitation is the sincerest form of flattery

    This answer is akin to @trenki except that it subtracts nearby struct tm values instead of assuming DST shift is 1 hour and time_t is in seconds.

    #include <limits.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <time.h>
    
    // return difference in **seconds** of the tm_mday, tm_hour, tm_min, tm_sec members.
    long tz_offset_second(time_t t) {
      struct tm local = *localtime(&t);
      struct tm utc = *gmtime(&t);
      long diff = ((local.tm_hour - utc.tm_hour) * 60 + (local.tm_min - utc.tm_min))
              * 60L + (local.tm_sec - utc.tm_sec);
      int delta_day = local.tm_mday - utc.tm_mday;
      // If |delta_day| > 1, then end-of-month wrap 
      if ((delta_day == 1) || (delta_day < -1)) {
        diff += 24L * 60 * 60;
      } else if ((delta_day == -1) || (delta_day > 1)) {
        diff -= 24L * 60 * 60;
      }
      return diff;
    }
    
    void testtz(void) {
      long off = -1;
      int delta = 600;
      for (time_t t = 0; t < LONG_MAX-delta; t+=delta) {
        long off2 = tz_offset_second(t);
    
        // Print time whenever offset changes.
        if (off != off2) {
          struct tm utc = *gmtime(&t);
          printf("%10jd %04d-%02d-%02dT%02d:%02d:%02dZ\n", (intmax_t) t,
                  utc.tm_year + 1900, utc.tm_mon + 1, utc.tm_mday,
                  utc.tm_hour, utc.tm_min, utc.tm_sec);
          struct tm local = *localtime(&t);
          off = off2;
          printf("%10s %04d-%02d-%02d %02d:%02d:%02d %2d %6ld\n\n", "",
                  local.tm_year + 1900, local.tm_mon + 1, local.tm_mday,
                  local.tm_hour, local.tm_min, local.tm_sec, local.tm_isdst ,off);
          fflush(stdout);
        }
      }
      puts("Done");
    }
    

    Output

                                      v----v  Difference in seconds
             0 1970-01-01T00:00:00Z
               1969-12-31 18:00:00  0 -21600
    
       5731200 1970-03-08T08:00:00Z
               1970-03-08 03:00:00  1 -18000
    
      26290800 1970-11-01T07:00:00Z
               1970-11-01 01:00:00  0 -21600
    
    ...
    
    2109222000 2036-11-02T07:00:00Z
               2036-11-02 01:00:00  0 -21600
    
    2120112000 2037-03-08T08:00:00Z
               2037-03-08 03:00:00  1 -18000
    
    2140671600 2037-11-01T07:00:00Z
               2037-11-01 01:00:00  0 -21600
    
    Done
    
    0 讨论(0)
  • 2021-02-20 08:03

    Does your system's strftime() function support the %z and %Z specifiers? On FreeBSD,

     %Z    is replaced by the time zone name.
    
     %z    is replaced by the time zone offset from UTC; a leading plus sign
           stands for east of UTC, a minus sign for west of UTC, hours and
           minutes follow with two digits each and no delimiter between them
           (common form for RFC 822 date headers).
    

    and I can use this to print this:

    $ date +"%Z: %z"
    CEST: +0200
    

    ISO C99 has this in 7.23.3.5 The strftime function:

    %z     is replaced by the offset from UTC in the ISO 8601 format
           ‘‘−0430’’ (meaning 4 hours 30 minutes behind UTC, west of Greenwich),
           or by no characters if no time zone is determinable. [tm_isdst]
    %Z     is replaced by the locale’s time zone name or abbreviation, or by no
           characters if no time zone is determinable. [tm_isdst]
    
    0 讨论(0)
  • 2021-02-20 08:08

    I would like to submit yet another answer to this question, one that AFAICS also deals with the IDL.

    This solution depends on timegm and mktime. On Windows timegm is available as _mkgmtime from the CRT, in other words define a conditional macro.

    #if _WIN32
    #    define timegm _mkgmtime
    #endif
    
    int local_utc_offset_minutes ( ) {
        time_t t  = time ( NULL );
        struct tm * locg = localtime ( &t );
        struct tm locl;
        memcpy ( &locl, locg, sizeof ( struct tm ) );
        return (int)( timegm ( locg ) - mktime ( &locl ) ) / 60;
    }
    
    0 讨论(0)
  • 2021-02-20 08:09

    This C code computes the local time offset in minutes relative to UTC. It assumes that DST is always one hour offset.

    #include <stdio.h>
    #include <time.h>
    
    int main()
    {
        time_t rawtime = time(NULL);
        struct tm *ptm = gmtime(&rawtime);
        time_t gmt = mktime(ptm);
        ptm = localtime(&rawtime);
        time_t offset = rawtime - gmt + (ptm->tm_isdst ? 3600 : 0);
    
        printf("%i\n", (int)offset);
    }
    

    It uses gmtime and localtime though. Why don't you want to use those functions?

    0 讨论(0)
  • 2021-02-20 08:13

    IMHO the only foolproof and portable way is to use localtime and gmtime and manually compute the delta in minute because those 2 functions exist on all known systems. For example:

    int deltam() {
        time_t t = time(NULL);
        struct tm *loc = localtime(&t);
        /* save values because they could be erased by the call to gmtime */
        int loc_min = loc->tm_min;
        int loc_hour = loc->tm_hour;
        int loc_day = loc->tm_mday;
        struct tm *utc = gmtime(&t);
        int delta = loc_min - utc->tm_min;
        int deltaj = loc_day - utc->tm_mday;
        delta += (loc_hour - utc->tm_hour) * 60;
        /* hack for the day because the difference actually is only 0, 1 or -1 */
        if ((deltaj == 1) || (deltaj < -1)) {
            delta += 1440;
        }
        else if ((deltaj == -1) || (deltaj > 1)) {
            delta -= 1440;
        }
        return delta;
    }
    

    Beware, I did not test all possible corner cases, but it could be a starting point for your requirement.

    0 讨论(0)
提交回复
热议问题