将人类可读日期转换为毫秒,然后再返回,而不会丢失信息

时间:2016-04-24 15:28:27

标签: c++ c++11 boost

我在程序中获得了人类可读的时间戳。我想将它们存储在数据库中,并以不同的方式使用它们。我更愿意将它们表示为长整理,因为它更容易,更有效,但是能够在机器读取的方便和人类的方便之间进行切换也很方便。

我已经将以下测试程序整合在一起,其中包括将人类可读时间戳转换为long和back的功能。

// 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函数应该如何工作而不会丢失小数秒信息。任何想法??

2 个答案:

答案 0 :(得分:4)

删除long之前的句号并且有效。请参阅此处的示例:http://www.boost.org/doc/libs/1_60_0/doc/html/date_time/date_time_io.html#time_input_facet_accessors

另请注意,毫秒不足以存储示例long的整体精度。由于你有6个十进制数字,你需要微秒。

另请注意,自1970年1月1日以来,存在巨大的微秒负载,因此uint64_t是不够的。最后使用boost并不是一个好主意,因为这种类型在不同的平台上可能有不同的大小。更好的想法是将$ perl -MO=Concise -e'sort' 5 <@> leave[1 ref] vKP/REFC ->(end) 1 <0> enter ->2 2 <;> nextstate(main 1 -e:1) v:{ ->3 4 <@> sort vK ->5 3 <0> pushmark s ->4 -e syntax OK 用于此目的,sort用于存储持续时间的内容。在64位的帮助下,您将能够将微秒存储在另外五百万年左右。

答案 1 :(得分:2)

为了防止有人想使用更新的C ++ 11 <chrono>类型来处理这样的问题,以下是使用此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