2010-11-09 2 views
8

У меня есть строка в следующем формате:С ++ Преобразование времени строки в секундах от эпохи

2010-11-04T23: 23: 01Z

Зет указывает на то, что время УНИВЕРСАЛЬНОЕ ГЛОБАЛЬНОЕ ВРЕМЯ.
Я бы предпочел сохранить это как эпоху, чтобы упростить сравнение.

Что такое рекомендуемый метод для этого?

В настоящее время (после quck поиска) алгоритм simplist является:

1: <Convert string to struct_tm: by manually parsing string> 
2: Use mktime() to convert struct_tm to epoch time. 

// Problem here is that mktime uses local time not UTC time. 
+0

_mkgmtime() в моем CRT. YMMV. –

+0

@Loki, нам не нужен метатег, чтобы поймать все, что является ОС-нейтральным. Пожалуйста, прекратите воссоздать тег. – Charles

+0

@Charles: Пожалуйста, прекратите меня менять свой вопрос. Я бы хотел, если бы у вас был намек на то, что, по моему мнению, редактирование недействительно после второго раза, когда я его изменил. –

ответ

6

Использование C++ 11 функциональных возможностей теперь мы можем использовать потоки для разбора раз:

iomanip std::get_time преобразует строку, основанную на наборе параметров формата и конвертировать их в struct tz объект.

Затем вы можете использовать std::mktime(), чтобы преобразовать это значение в эпоху.

#include <iostream> 
#include <sstream> 
#include <locale> 
#include <iomanip> 

int main() 
{ 
    std::tm t = {}; 
    std::istringstream ss("2010-11-04T23:23:01Z"); 

    if (ss >> std::get_time(&t, "%Y-%m-%dT%H:%M:%S")) 
    { 
     std::cout << std::put_time(&t, "%c") << "\n" 
        << std::mktime(&t) << "\n"; 
    } 
    else 
    { 
     std::cout << "Parse failed\n"; 
    } 
} 
1

Вы можете использовать boost::date_time и написать небольшой ручной анализатор (возможно регулярное выражение на основе) для ваших строк.

4

Вы можете использовать такую ​​функцию, как strptime, чтобы преобразовать строку в struct tm вместо того, чтобы разбор вручную.

3

Это не точный дубликат, но вы найдете ответ @ Cubbi от here полезным, я ставлю. Это подразумевает ввод UTC.

Boost также поддерживает прямое преобразование из ISO 8601 через boost::posix_time::from_iso_string, которое вызывает boost::date_time::parse_iso_time, и здесь вы просто разделите трейлинг «Z» и обрабатываете TZ как неявный UTC.

#include <iostream> 
#include <boost/date_time.hpp> 

namespace bt = boost::posix_time; 

const std::locale formats[] = { 
std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%d %H:%M:%S")), 
std::locale(std::locale::classic(),new bt::time_input_facet("%Y/%m/%d %H:%M:%S")), 
std::locale(std::locale::classic(),new bt::time_input_facet("%d.%m.%Y %H:%M:%S")), 
std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%d"))}; 
const size_t formats_n = sizeof(formats)/sizeof(formats[0]); 

std::time_t pt_to_time_t(const bt::ptime& pt) 
{ 
    bt::ptime timet_start(boost::gregorian::date(1970,1,1)); 
    bt::time_duration diff = pt - timet_start; 
    return diff.ticks()/bt::time_duration::rep_type::ticks_per_second; 

} 
void seconds_from_epoch(const std::string& s) 
{ 
    bt::ptime pt; 
    for(size_t i=0; i<formats_n; ++i) 
    { 
     std::istringstream is(s); 
     is.imbue(formats[i]); 
     is >> pt; 
     if(pt != bt::ptime()) break; 
    } 
    std::cout << " ptime is " << pt << '\n'; 
    std::cout << " seconds from epoch are " << pt_to_time_t(pt) << '\n'; 
} 
int main() 
{ 
    seconds_from_epoch("2004-03-21 12:45:33"); 
    seconds_from_epoch("2004/03/21 12:45:33"); 
    seconds_from_epoch("23.09.2004 04:12:21"); 
    seconds_from_epoch("2003-02-11"); 
} 
6

Это ISO8601 формат. Вы можете использовать функцию strptime, чтобы проанализировать ее с аргументом %FT%T%z. Он не является частью C++ Standard, хотя вы можете использовать его с открытым исходным кодом (например, this).

1

Проблема заключается в том, что mktime использует местное время, а не время UTC.

Как рассчитать разницу во времени между временем по Гринвичу и местному времени, а затем добавить его к значению, возвращаемому mktime?

time_t local = time(NULL), 
     utc = mktime(gmtime(&local)); 
int diff = utc - local; 
1

Что случилось с strptime()?

И на Linux, вы даже получить «секунд к востоку от ЮТК» поле избавляя вас от какой-либо необходимости разобрать:

#define _XOPEN_SOURCE 
#include <iostream> 
#include <time.h> 

int main(void) { 

    const char *timestr = "2010-11-04T23:23:01Z"; 

    struct tm t; 
    strptime(timestr, "%Y-%m-%dT%H:%M:%SZ", &t); 

    char buf[128]; 
    strftime(buf, sizeof(buf), "%d %b %Y %H:%M:%S", &t); 

    std::cout << timestr << " -> " << buf << std::endl; 

    std::cout << "Seconds east of UTC " << t.tm_gmtoff << std::endl; 
} 

которая для меня дает

/tmp$ g++ -o my my.cpp 
/tmp$ ./my 
2010-11-04T23:23:01Z -> 04 Nov 2010 23:23:01 
Seconds east of UTC 140085769590024 
+1

Значение «Секунды к востоку от UTC» явно фиктивное, поскольку показанное значение превышает 4 миллиона лет. Часовой пояс должен быть в пределах +/- 50000 секунд UTC. – caf

+0

Darn. Ты прав. И даже когда я определяю _BSD_SOURCE (что упоминала страница руководства «mktime/ctime»), она все еще выглядит слишком большой. Мне не хватает чего-то другого. –

0

X/Open обеспечивает global timezone переменная, которая указывает количество секунд, в течение которых местное время находится за UTC. Вы можете использовать это, чтобы настроить выход из mktime():

#define _XOPEN_SOURCE 
#include <stdio.h> 
#include <time.h> 

/* 2010-11-04T23:23:01Z */ 
time_t zulu_time(const char *time_str) 
{ 
    struct tm tm = { 0 }; 

    if (!strptime(time_str, "%Y-%m-%dT%H:%M:%SZ", &tm)) 
     return (time_t)-1; 

    return mktime(&tm) - timezone; 
} 
3

Проблема здесь в том, что использует местное указываете ей время не времени UTC.

Linux предоставляет timegm то, что вы хотите (т.

Вот мое решение, которое я заставил принять только «Зулу» (часовой пояс Z). Примечание , что strptime на самом деле не выглядит правильно разобранным часовым поясом, даже , хотя, похоже, у glib есть определенная поддержка. Вот почему я просто бросаю исключение , если строка не заканчивается на 'Z'.

static double EpochTime(const std::string& iso8601Time) 
{ 
    struct tm t; 
    if (iso8601Time.back() != 'Z') throw PBException("Non Zulu 8601 timezone not supported"); 
    char* ptr = strptime(iso8601Time.c_str(), "%FT%T", &t); 
    if(ptr == nullptr) 
    { 
     throw PBException("strptime failed, can't parse " + iso8601Time); 
    } 
    double t2 = timegm(&t); // UTC 
    if (*ptr) 
    { 
     double fraction = atof(ptr); 
     t2 += fraction; 
    } 
    return t2; 
} 
2

Новый ответ на старый вопрос. Обоснование для нового ответа: если вы хотите использовать типы <chrono> для решения такой проблемы.

В дополнение к C++ 11/C++ 14, вы будете нуждаться в этом free, open source date/time library:

#include "tz.h" 
#include <iostream> 
#include <sstream> 

int 
main() 
{ 
    std::istringstream is("2010-11-04T23:23:01Z"); 
    is.exceptions(std::ios::failbit); 
    date::sys_seconds tp; 
    date::parse(is, "%FT%TZ", tp); 
    std::cout << "seconds from epoch is " << tp.time_since_epoch().count() << "s\n"; 
} 

Эта программа выводит:

seconds from epoch is 1288912981s 

При неудачной попытке анализа в любом случае, будет выбрано исключение. Если вы предпочитаете не делать исключений, не делайте is.exceptions(std::ios::failbit);, но вместо этого проверяйте is.fail().

Смежные вопросы