使用strptime / strftime的结果不一致

时间:2016-03-25 23:50:21

标签: c++ datetime ubuntu-14.04

我编写了一个代表Date的C ++类,我使用strptime / strftime来编写和实例化来自字符串的日期。

The full code is here

当我使用选项卡中的bash"示例输出"在我的linux上运行它几次时,有时我创建并解析了相同的日期,有时我得到一个小时的转移日期(我的时区是UTC + 1)。

那么,这里发生了什么,我不知道!

#ifndef DOLIPRANE_TIMEUNIT_HPP
#define DOLIPRANE_TIMEUNIT_HPP

enum TimeUnit  {
    DAY,
    HOUR,
    MINUTE,
    SECOND
  };
#endif
#ifndef DOLIPRANE_DATE_HPP
#define DOLIPRANE_DATE_HPP

#include <ctime>
#include <string>


class Date
{
public:

  Date();
  Date(time_t epoch);

  /**
   * Expected format: dd/MM/YYYY HH:mm:[ss]
   */
  Date(const std::string &date);

  ~Date();

  void
  add(long val, TimeUnit u = SECOND);


  bool
  operator==(const Date &other) const;

  bool
  operator!=(const Date &other) const;

  bool
  operator<(const Date &other) const;

  bool
  operator<=(const Date &other) const;

  bool
  operator>(const Date &other) const;

  bool
  operator>=(const Date &other) const;

  friend std::ostream&
  operator<<(std::ostream &, const Date&);

  friend std::istream&
  operator>>(std::istream &, Date&);

private:

  static const std::string FORMAT;

  time_t m_time;


};
#endif


#include <iostream>
#include <stdexcept>
#include <ctime>

const char SEPARATOR=';';

const std::string Date::FORMAT="%d/%m/%Y %H:%M:%S";

Date::Date()
{
  m_time = time(NULL);
}

Date::Date(time_t epoch) 
  : m_time(epoch)
{}

Date::Date(const std::string &date)
{
  struct tm t;
  const char* ptr = strptime(date.c_str(), FORMAT.c_str(), &t);
  if (!ptr) {
    std::string cause = "Cannot parse date ";
    cause += date;
    throw std::invalid_argument(cause);
  }
  m_time = mktime(&t);
  if (m_time == -1) {
    std::string cause = "Cannot compute epoch from " + date;
    throw std::range_error(cause);
  }
}

Date::~Date()
{
}

void
Date::add(long val, TimeUnit u) {

  switch(u){
  case DAY:
    m_time += 86400*val;
    break;
  case HOUR:
    m_time += 3600*val;
    break;
  case MINUTE:
    m_time += 60*val;
    break;
  case SECOND:
    m_time += val;
    break;
  default:
    throw std::invalid_argument("Unknown TimeUnit specified");
  }
}

bool
Date::operator==(const Date& o) const
{
  return m_time == o.m_time;
}

bool
Date::operator!=(const Date& o) const
{
  return ! (*this==o);
}

bool
Date::operator<(const Date &other) const
{
  return m_time < other.m_time;
}

bool
Date::operator<=(const Date &other) const
{
  return m_time <= other.m_time;
}

bool
Date::operator>(const Date &other) const
{
  return m_time > other.m_time;
}

bool
Date::operator>=(const Date &other) const
{
  return m_time >= other.m_time;
}

std::ostream&
operator<<(std::ostream& out, const Date &d)
{
  struct tm* tm = localtime(&d.m_time);
  char buffer[20];
  strftime(buffer, 20, Date::FORMAT.c_str(), tm);
  out << buffer << SEPARATOR;
  return out;
}

std::istream&
operator>>(std::istream &in, Date &d)
{
  std::string buf;
  std::getline(in, buf, SEPARATOR);
  Date o(buf);
  d = o;
  return in;
}

#include <iostream>
#include <fstream>

int
main(void)
{
  Date d;
  std::cout << d << std::endl;
  std::ofstream out("tmp.txt");
  out << d;
  out.close();

  std::ifstream in("tmp.txt");
  Date d2;
  in >> d2;
  in.close();
  std::cout << d2 << std::endl;

}

最后,我是如何测试的:

$ for i in `seq 1 10`; do echo "test $i:"; ./test; rm tmp.txt; done
test 1:
26/03/2016 00:30:31;
26/03/2016 00:30:31;
test 2:
26/03/2016 00:30:31;
26/03/2016 00:30:31;
test 3:
26/03/2016 00:30:31;
25/03/2016 23:30:31;
test 4:
26/03/2016 00:30:31;
26/03/2016 00:30:31;
test 5:
26/03/2016 00:30:31;
25/03/2016 23:30:31;
test 6:
26/03/2016 00:30:31;
26/03/2016 00:30:31;
test 7:
26/03/2016 00:30:31;
25/03/2016 23:30:31;
test 8:
26/03/2016 00:30:31;
25/03/2016 23:30:31;
test 9:
26/03/2016 00:30:31;
25/03/2016 23:30:31;
test 10:
26/03/2016 00:30:31;
26/03/2016 00:30:31;

2 个答案:

答案 0 :(得分:4)

问题是 struct tm t;
在采用字符串的Date构造函数中, 从不初始化对象,因此其tm_isdst字段具有未指定的值。正确设置tm_isdst字段,您将获得一致 结果多次运行。

答案 1 :(得分:3)

最有可能的问题是夏令时(也称为夏令时)。

SELECT First(Salesmen.FirstName) As FN, Fist(Salesmen.LastName) As LN, SUM (ProductsSales.QuantitySold) As TotalProd FROM Salesmen INNER JOIN ProductsSales ON Salesmen.SalesmanId = ProductsSales.SellerId WHERE (Salesmen.FirstName= 'Boris' AND Salesmen.LastName = 'Davidovich') GROUP BY Salesmen.SalesmanID 有一个名为struct tm的字段。该字段中的正值表示故障时间为DST。 0表示它不是DST。

tm_isdst会正确设置字段,但strftime不会触及它(至少在glibc实现中)。 strptime期望它被正确设置,但幸运的是它允许将其设置为负值,这意味着“我不知道”。在这种情况下,mktime会尝试解决这个问题。 (这可能是不可能的,因为每年有一个小时重复,一次是夏季时间,一次是时间变化后的冬季时间。)

保留mktime字段未初始化,就像在代码中一样,是未定义(和不可预测)的行为。通常正确的策略是在调用tm_isdst之后和调用strptime之前将其设置为-1。

通常最佳做法是在调用mktime之前将struct tm清零或初始化,因为该函数仅设置与格式对应的字段。