Correct C / C ++ timezone conversion (up to seconds from an era)

I would like to know the time as seconds from the era. It is noteworthy that I would not want to matter where the machine performing the conversion should be sufficient for the time zone line.

I have this test program, pt.cc:

#include <assert.h>
#include <errno.h>
#include <iostream>
#include <stdio.h>
#include <string>
#include <string.h>
#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE
#endif
#include <time.h>

using namespace std;  // To be brief, don't do this in real life.

int main(int argc, char* argv[]) {
    (void)argc; (void)argv;   // Skip compile warning.

    // I expect both of these to transform to 1440671500.
    cout << "1440671500 expected" << endl;
    const char utc_example[] = "2015-08-27T11:31:40+0100";
    struct tm tm;
    memset(&tm, 0, sizeof(struct tm));
    char* end = strptime(utc_example, "%Y-%m-%dT%H:%M:%S%z", &tm);
    assert(end);
    assert(*end == '\0');
    time_t seconds_since_epoch = mktime(&tm);
    cout << "utc example: " << seconds_since_epoch << "  or maybe  "
         << seconds_since_epoch - tm.tm_gmtoff + (tm.tm_isdst ? 3600 : 0) << endl;

    const char tz_example[] = "2015-08-27T10:31:40Z";
    memset(&tm, 0, sizeof(struct tm));
    end = strptime(tz_example, "%Y-%m-%dT%H:%M:%S%nZ", &tm);
    assert(end);
    assert(*end == '\0');
    seconds_since_epoch = mktime(&tm);
    cout << " tz example: " << seconds_since_epoch << "  or maybe  "
         << seconds_since_epoch - tm.tm_gmtoff + (tm.tm_isdst ? 3600 : 0) << endl;

    return 0;
}

This is the conclusion:

jeff@birdsong:tmp $ clang++ -ggdb3 -Wall -Wextra -std=c++14 pt.cc -o pt
jeff@birdsong:tmp $ ./pt
1440671500 expected
utc example: 1440671500  or maybe  1440667900
 tz example: 1440667900  or maybe  1440664300
jeff@birdsong:tmp $ TZ=America/New_York ./pt
1440671500 expected
utc example: 1440693100  or maybe  1440711100
 tz example: 1440689500  or maybe  1440707500
jeff@birdsong:tmp $ TZ=Europe/London ./pt
1440671500 expected
utc example: 1440675100  or maybe  1440675100
 tz example: 1440671500  or maybe  1440671500
jeff@birdsong:tmp $ 

Notice how the return value changes mktime()depending on the surrounding time zone. The man page entry for mktime()assumes that it interprets the breakdown time as local time. So I tried to subtract the GMT offset and compensate for the time zone if it ignores these values ​​(the value "or maybe").

Any tips on how to do this correctly? (If that matters, I only need this to work on Linux.)

+4
4

, , Google https://github.com/google/cctz

#include <chrono>
#include <iostream>
#include <string>

#include "src/cctz.h"

using namespace std;

int main(int argc, char* argv[]) {
  const char kFmt[] = "%Y-%m-%dT%H:%M:%S%Ez";

  // I expect both of these to transform to 1440671500.
  const char utc_example[] = "2015-08-27T11:31:40+0100";
  const char tz_example[] = "2015-08-27T10:31:40Z";
  cout << "1440671500 expected" << endl;

  // Required by cctz::Parse(). Only used if the formatted
  // time does not include offset info.
  const auto utc = cctz::UTCTimeZone();

  std::chrono::system_clock::time_point tp;
  if (!Parse(kFmt, utc_example, utc, &tp)) return -1;
  cout << "utc example: " << std::chrono::system_clock::to_time_t(tp) << "\n";

  if (!Parse(kFmt, tz_example, utc, &tp)) return -1;
  cout << " tz example: " << std::chrono::system_clock::to_time_t(tp) << "\n";

  return 0;
}

:

1440671500 expected
utc example: 1440671500
 tz example: 1440671500

, , / , , time_t, , " ", . , 12:30 CppCon: https://youtu.be/2rnIHsqABfM?t=12m30s

+2

:

http://howardhinnant.imtqy.com/date/date.html

, , , API / C. , C , .

. , , .

. :

#include "chrono_io.h"
#include "date.h"
#include <iostream>
#include <string>
#include <sstream>

using second_point = std::chrono::time_point<std::chrono::system_clock,
                                             std::chrono::seconds>;

std::chrono::minutes
parse_offset(std::istream& in)
{
    using namespace std::chrono;
    char c;
    in >> c;
    minutes result = 10*hours{c - '0'};
    in >> c;
    result += hours{c - '0'};
    in >> c;
    result += 10*minutes{c - '0'};
    in >> c;
    result += minutes{c - '0'};
    return result;
}

second_point
parse(const std::string& str)
{
    std::istringstream in(str);
    in.exceptions(std::ios::failbit | std::ios::badbit);
    int yi, mi, di;
    char dash;
    // check dash if you're picky
    in >> yi >> dash >> mi >> dash >> di;
    using namespace date;
    auto ymd = year{yi}/mi/di;
    // check ymd.ok() if you're picky
    char T;
    in >> T;
    // check T if you're picky
    int hi, si;
    char colon;
    in >> hi >> colon >> mi >> colon >> si;
    // check colon if you're picky
    using namespace std::chrono;
    auto h = hours{hi};
    auto m = minutes{mi};
    auto s = seconds{si};
    second_point result = sys_days{ymd} + h + m + s;
    char f;
    in >> f;
    if (f == '+')
        result -= parse_offset(in);
    else if (f == '-')
        result += parse_offset(in);
    else
        ;// check f == 'Z' if you're picky
    return result;
}

int
main()
{
    using namespace date;
    std::cout << parse("2015-08-27T11:31:40+0100").time_since_epoch() << '\n';
    std::cout << parse("2015-08-27T10:31:40Z").time_since_epoch() << '\n';
}

, std::istringstream, std::chrono, - .

, , ( ). , , - , , - : ( ).

. , , ( ) . , . // chrono::time_point days.

. , , .

:

1440671500s
1440671500s

"date.h" parse:

date::sys_seconds
parse(const std::string& str)
{
    std::istringstream in(str);
    date::sys_seconds tp;
    in >> date::parse("%FT%TZ", tp);
    if (in.fail())
    {
        in.clear();
        in.str(str);
        in >> date::parse("%FT%T%z", tp);
    }
    return tp;
}

int
main()
{
    using namespace date;
    std::cout << parse("2015-08-27T11:31:40+0100").time_since_epoch() << '\n';
    std::cout << parse("2015-08-27T10:31:40Z").time_since_epoch() << '\n';
}

.

+5

, 1970 UTC:

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

time_t GetSecondsSinceNewYearsDay1970UTC()
{
    struct timeval tv;
    if (gettimeofday(&tv, NULL) != 0) perror("doh!");
    return tv.tv_sec;
}

, , UTC, , .

time_t GetSecondsSinceNewYearsDay1970LocalTimeZone()
{
   const time_t utcTime = GetSecondsSinceNewYearsDay1970UTC();
   time_t now = time(NULL);
   time_t localTime = utcTime;  // will be adjusted below
   struct tm gmtm;
   struct tm * tm = gmtime_r(&now, &gmtm);
   if (tm)
   {
      localTime += (now-mktime(tm));
      if (tm->tm_isdst>0) localTime += (60*60);  // add an hour for DST
   }
   return localTime;
}
0

Before calling the mktime()returned object, strptime()set tm->tm_isdstto a negative number so that the library determines if the DST was in effect at that time. Please note that the time zone offset to strptime()is a non-standard extension. (Also note that using a structure name as a variable name is a bad style in C ++, as it creates ambiguity about that tm.)

0
source

Source: https://habr.com/ru/post/1613700/


All Articles