2016-04-24 4 views
3

Я получаю человеческие читаемые временные метки в свою программу. Я хочу хранить их в базе данных и работать с ними по-разному. Я бы предпочел представить их как долгое время, поскольку это проще и эффективнее, но это также удобно, когда вы можете переключаться между тем, что удобно для читаемой машины и что полезно для человека.Преобразование человекочитаемых дат в миллисекунды и обратно без потери информации

Я взломал следующую тестовую программу, включающую функции, которые можно преобразовать в человеческие читаемые временные метки и обратно, и обратно.

// g++ -o timetest timetest.cpp -std=c++11 

#include <iostream> 
#include <sstream> 

#include <boost/date_time.hpp> 
#include <boost/date_time/posix_time/posix_time.hpp> 
#include <boost/date_time/posix_time/posix_time_io.hpp> 

long millis_from_date(const std::string& s) 
{ 
    boost::posix_time::ptime pt; 
    std::istringstream is(s); 
    auto* f = new boost::posix_time::time_input_facet("%Y-%m-%dT%H:%M:%S.%FZ"); 
    std::locale loc(std::locale(""), f); 
    is.imbue(loc); 
    is >> pt; 
    boost::posix_time::ptime timet_start(boost::gregorian::date(1970,1,1)); 
    boost::posix_time::time_duration diff = pt - timet_start; 
    return diff.total_milliseconds(); 
} 

std::string date_from_millis(long ms) 
{ 
    static const boost::posix_time::ptime epoch(boost::gregorian::date(1970,1,1)); 
    boost::posix_time::time_facet * facet = new boost::posix_time::time_facet("%Y-%m-%dT%H:%M:%S.%fZ"); 
    std::ostringstream stream; 
    stream.imbue(std::locale(stream.getloc(), facet)); 
    stream << epoch + boost::posix_time::milliseconds(ms);; 
    return stream.str(); 
} 

int main() 
{ 
    std::string time = "2016-04-14T07:47:50.120043Z"; 

    std::cout << "Initial input:   " << time << std::endl; 
    std::cout << std::endl; 

    { 
    long millis = millis_from_date(time); 
    std::cout << "Initial input in millis: " << millis << std::endl; 

    std::string newtime = date_from_millis(millis); 
    std::cout << "Converted back to date: " << newtime << std::endl; 
    } 

    { 
    long millis = millis_from_date(time); 
    std::cout << "Initial input in millis: " << millis << std::endl; 

    std::string newtime = date_from_millis(millis); 
    std::cout << "Converted back to date: " << newtime << std::endl; 
    } 

    return 0; 
} 

Вот пример вывода.

Initial input:   2016-04-14T07:47:50.120043Z 

Initial input in millis: 1460620070000 
Converted back to date: 2016-04-14T07:47:50.000000Z 
Initial input in millis: 1460620070000 
Converted back to date: 2016-04-14T07:47:50.000000Z 

Как вы можете видеть, при преобразовании в миллисекунды, дробно вторая информация теряется, так что вы получите секунды, так как начало эпохи с 000 прикрепили на конце. Таким образом, при преобразовании полученного длинного назад в человекообразную метку времени теряется дробная вторая информация.

Я уже пробовал немало вещей, и я не могу понять, как должна работать функция millis_from_date, не теряя дробную вторую информацию. Есть идеи??

ответ

4

Удалить период до %F, и он работает. См. Пример здесь: http://www.boost.org/doc/libs/1_60_0/doc/html/date_time/date_time_io.html#time_input_facet_accessors

Обратите внимание, что миллисекунд недостаточно для хранения всей точности из вашего примера 2016-04-14T07:47:50.120043Z. Поскольку у вас есть 6 десятичных цифр, вам понадобятся микросекунды.

Также обратите внимание, что с 01 января по 1970 год существует огромная нагрузка микросекунд, поэтому long недостаточно. И вообще не рекомендуется использовать long, так как этот тип может иметь разный размер на разных платформах. Лучшей идеей было бы использовать uint64_t для этой цели, вот что boost использует для хранения длительности времени. С помощью 64 бит вы сможете хранить микросекунды еще полмиллиона лет или около того.

+0

Спасибо Михаил, я последовал вашему советовать и теперь все работает точно так же, как это должно быть. – user6247858

+0

@ user6247858 Добро пожаловать! – Mikhail

2

Только в случае, если кто-то хотел бы работать с проблемой, как это, используя новые 11 <chrono> C++ типы, вот как вы могли бы сделать это с помощью этого free open source library, чтобы помочь с форматированием и разбором дробных секунд:

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

std::chrono::milliseconds 
millis_from_date(const std::string& s) 
{ 
    using namespace std::chrono; 
    using sys_milliseconds = time_point<system_clock, milliseconds>; 
    sys_milliseconds pt; 
    std::istringstream is(s); 
    date::parse(is, "%FT%TZ", pt); 
    return pt.time_since_epoch(); 
} 

std::string 
date_from_millis(std::chrono::milliseconds ms) 
{ 
    using namespace std::chrono; 
    using sys_milliseconds = time_point<system_clock, milliseconds>; 
    return date::format("%FT%TZ", sys_milliseconds{ms}); 
} 

int 
main() 
{ 
    std::string time = "2016-04-14T07:47:50.120043Z"; 

    std::cout << "Initial input:   " << time << std::endl; 
    std::cout << std::endl; 

    { 
    auto millis = millis_from_date(time); 
    std::cout << "Initial input in millis: " << millis.count() << std::endl; 

    std::string newtime = date_from_millis(millis); 
    std::cout << "Converted back to date: " << newtime << std::endl; 
    } 

    { 
    auto millis = millis_from_date(time); 
    std::cout << "Initial input in millis: " << millis.count() << std::endl; 

    std::string newtime = date_from_millis(millis); 
    std::cout << "Converted back to date: " << newtime << std::endl; 
    } 
} 

, который выводит:

Initial input:   2016-04-14T07:47:50.120043Z 

Initial input in millis: 1460620070120 
Converted back to date: 2016-04-14T07:47:50.120Z 
Initial input in millis: 1460620070120 
Converted back to date: 2016-04-14T07:47:50.120Z 

Как отмечалось в Mikhail's excellent answer, если мы действительно хотим быть без потерь здесь, мы должны трафика в микросекунд вместо миллисекунд. Вот как приведенный выше код будет изменен, чтобы сделать это:

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

std::chrono::microseconds 
micros_from_date(const std::string& s) 
{ 
    using namespace std::chrono; 
    using sys_microseconds = time_point<system_clock, microseconds>; 
    sys_microseconds pt; 
    std::istringstream is(s); 
    date::parse(is, "%FT%TZ", pt); 
    return pt.time_since_epoch(); 
} 

std::string 
date_from_micros(std::chrono::microseconds ms) 
{ 
    using namespace std::chrono; 
    using sys_microseconds = time_point<system_clock, microseconds>; 
    return date::format("%FT%TZ", sys_microseconds{ms}); 
} 

int 
main() 
{ 
    std::string time = "2016-04-14T07:47:50.120043Z"; 

    std::cout << "Initial input:   " << time << std::endl; 
    std::cout << std::endl; 

    { 
    auto micros = micros_from_date(time); 
    std::cout << "Initial input in micros: " << micros.count() << std::endl; 

    std::string newtime = date_from_micros(micros); 
    std::cout << "Converted back to date: " << newtime << std::endl; 
    } 

    { 
    auto micros = micros_from_date(time); 
    std::cout << "Initial input in micros: " << micros.count() << std::endl; 

    std::string newtime = date_from_micros(micros); 
    std::cout << "Converted back to date: " << newtime << std::endl; 
    } 
} 

, который выводит:

Initial input:   2016-04-14T07:47:50.120043Z 

Initial input in micros: 1460620070120043 
Converted back to date: 2016-04-14T07:47:50.120043Z 
Initial input in micros: 1460620070120043 
Converted back to date: 2016-04-14T07:47:50.120043Z 
Смежные вопросы