简体   繁体   English

C ++:将日期/时间字符串转换为tm结构

[英]C++ : convert date/time string to tm struct

Consider this somewhat of a follow up to this question . 考虑一下这个问题的后续问题 Essentially, the C++ date/time formatting facilities seem to be hopelessly broken - so much so that in order to do something as simple as convert a date/time string into an object, you really have to resort to either Boost.Datetime or good old C strftime / strptime facilities. 从本质上讲,C ++日期/时间格式化设施似乎无可救药地被打破 - 以至于为了做一些简单的事情,如将日期/时间字符串转换为对象,你真的不得不求助于Boost.Datetime或者旧的C strftime / strptime设施。

The problem is that neither of these solutions work directly with the C++ locale settings imbued onto a particular iostream object. 问题是这些解决方案都不能直接使用嵌入特定iostream对象的C ++语言环境设置。 The C facilities use the global C/POSIX locale settings, whereas the I/O facilities in Boost.Datetime seem to bypass iostream locale settings entirely by allowing the user to directly set the names of months, weekdays, etc., regardless of locale. C工具使用全局C / POSIX语言环境设置,而Boost.Datetime中的I / O工具似乎完全绕过iostream语言环境设置,允许用户直接设置月份,工作日等的名称,而不管语言环境如何。

So, I wanted something that would respect the locale settings imbued onto a particular I/O stream that would allow me to convert a string into a struct tm . 因此,我想要一些能够尊重特定I / O流上的语言环境设置的东西,这些设置允许我将字符串转换为struct tm It seemed easy enough, but I ran into obstacles around every corner. 这似乎很容易,但我遇到了各个角落的障碍。 At first, I noticed that some implementations of the STL provide a non-standard std::time_get::get function, so I decided to implement something similar. 起初,我注意到STL的一些实现提供了非标准的std::time_get::get函数,所以我决定实现类似的东西。 Basically, I would simply iterate over the format string and whenever I hit a format flag, I would use one of the time_get facilities (like get_monthname, get_weekday, get_year, etc.) to convert the input string into a struct tm . 基本上,我只是迭代格式字符串,每当我点击格式标志时,我会使用time_get工具之一(如get_monthname,get_weekday,get_year等)将输入字符串转换为struct tm This seems easy enough, except each one of these functions requires an exact iterator range. 这似乎很容易,除了这些函数中的每一个都需要精确的迭代器范围。 You can't convert "Monday," , it has to be "Monday" exactly, or the conversion fails. 你不能转换"Monday," ,它必须完全是"Monday" ,否则转换失败。 Since the iterators have to be istreambuf_iterator , you can't simply scan ahead, because each increment changes the get position in the stream buffer. 由于迭代器必须是istreambuf_iterator ,因此不能简单地向前扫描,因为每个增量都会更改流缓冲区中的get位置。 So, basically you have to first iterate over the stream, copying each character into another streambuffer, and then when you hit a delimiter (like a space or a comma), use the second streambuffer with the time_get facilities. 所以,基本上你必须首先遍历流,将每个字符复制到另一个 streambuffer,然后当你点击分隔符(如空格或逗号)时,使用带有time_get工具的第二个streambuffer。 It's literally as if the C++ designers went out of their way to make this as annoying as possible. 这就好像C ++设计师竭尽全力让它尽可能地烦人。

So, is there an easier solution? 那么,有更简单的解决方案吗? What do most C++ programmers do when they need to convert a date/time string to an object? 大多数C ++程序员在需要将日期/时间字符串转换为对象时会做什么? Do we just have to use the C facilities, and lose the advantages that come along with different locale settings imbued on different iostream objects? 我们是否只需要使用C设施,并且失去了在不同的iostream对象上出现的不同区域设置所带来的优势?

Boost uses the standard locale(s) by default; Boost默认使用标准语言环境; you don't have to bypass anything: 你不必绕过任何东西:

#include "boost/date_time/gregorian/gregorian.hpp"
#include <iostream>
#include <sstream>
#include <ctime>

int main(){
  using namespace boost::gregorian;

  std::locale::global(std::locale(""));
  std::locale german("German_Germany");
  std::locale french("French_France");

  date d1(day_clock::local_day());
  date d2;
  std::stringstream ss("2002-May-01");

  std::cout << "Mine: " << d1 << " | ";
  ss >> d2;
  std::cout << d2 << '\n';

  std::cout.imbue(german);
  std::cout << "Germany: " << d1 << " | ";
  ss.imbue(german);
  ss << "2002-Mai-01";
  ss >> d2;
  std::cout << d2 << '\n';

  std::cout.imbue(french);
  std::cout << "France: " << d1 << " | " << d2 << '\n';

  std::tm t = to_tm(d1);
  std::cout << "tm: " << asctime(&t);
}

(Those locale names are specific to Windows, of course.) Output: (当然,这些区域设置名称特定于Windows。)输出:

Mine: 2010-Oct-28 | 2002-May-01
Germany: 2010-Okt-28 | 2002-Mai-01
France: 2010-oct.-28 | 2002-mai-01
tm: Thu Oct 28 00:00:00 2010

Why not use the C library? 为什么不使用C库? It's almost certainly available in your implementation, and it's well debugged and tested. 它几乎可以在您的实现中使用,并且经过良好的调试和测试。

If it's lacking some feature, surely it's easier to make wrapper function which does the timezone work you desire. 如果它缺少某些功能,肯定会更容易制作包装功能,这样可以实现您想要的时区工作。

I always try to use locale independent strings to serialize data. 我总是尝试使用与语言环境无关的字符串来序列化数据。 Makes life a lot easier. 让生活更轻松。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM