5

I'm trying to parse a date formatted as YYMMDD. As a test, I've tried the following code:

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

int main(){
    std::tm t = {};
    std::istringstream ss("191203");
    ss >> std::get_time(&t, "%y%m%d");
    if (ss.fail()){ 
        std::cout << "Parse failed\n";
    } else {
        std::cout << std::put_time(&t, "%c") << '\n';
    }
}

Tested with Coliru, GCC 6.1 (C++17), the output is:

Sun Mar  0 00:00:00 1912

What I expected is:

Mon Dec 3 00:00:00 2019

Is there something wrong in the format string?

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
Valeriop84
  • 125
  • 1
  • 6
  • 2
    I see the same on GCC 6.3.0 - looks like a bug to me (treating `%y` as `%Y` - and if I pass `"20191203"` I get the March 2019 result that we want). Other bugs include `"19.1203"` with `"%y.%m%d"` -> `Sun Dec 3 00:00:00 1919`, so I think this implementation is in urgent need of a test suite! – Toby Speight Mar 21 '17 at 09:46
  • Related: http://stackoverflow.com/questions/21172767/parsing-an-date-time-string-with-stdget-time-needs-separators/21176925 – Marco A. Mar 21 '17 at 09:50
  • Update: same test, but parsing `"19"` with format `"%y"` gives output `Sun Jan 0 00:00:00 1919` – Valeriop84 Mar 21 '17 at 13:21
  • This use of a 2-digit year is *not* a proper ISO 8601 format. [The Wikipedia page](https://en.wikipedia.org/wiki/ISO_8601) says the standard requires 4-digit years. To quote: *ISO 8601 prescribes, as a minimum, a four-digit year [YYYY] to avoid the year 2000 problem.* – Basil Bourque May 10 '17 at 23:44

2 Answers2

1

You could use Howard Hinnant's free, open-source date/time library like this:

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

int
main()
{
    date::sys_days t;
    std::istringstream ss("191203");
    ss >> date::parse("%y%m%d", t);
    if (ss.fail())
        std::cout << "Parse failed\n";
    else
        std::cout << date::format("%c", t) << '\n';
}

It works on gcc 6.1: http://melpon.org/wandbox/permlink/gy3wpMXeCoxk9Ykj (as well as other platforms). Except the correct output is:

Tue Dec  3 00:00:00 2019
Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • Thank you for your reply. The result seems correct, but the parsing fails with the string `"19"` and format `"%y"`. It also fails with `"1912"` and format `"%y%m"`. If I'm not wrong, these are all ISO 8601 basic date formats. – Valeriop84 Mar 21 '17 at 15:21
  • @Valeriop84: All true. The design of this library has it that the coarsest `std::chrono::time_point` parsable is one with a precision of days, and that any ambiguity in nailing down the intention to a specific day is flagged as a failure. However this library does have `year` and `year_month` types which could be accepted by the parser and formatter with a little work. I will give that some thought, thanks. – Howard Hinnant Mar 21 '17 at 15:29
  • I still contend that there is a discrepancy between get_time and put_time (get_time has the weird rules because of Y2K paranoia) and put_time doesn't. So IMO your library is fine. POSIX code will break when 2068 comes around, so who has more future proofability? – user7744704 Mar 21 '17 at 15:32
  • @user7744704: I'm not a fan of `%y` either. I followed the POSIX spec when implementing it. But I don't really recommend its use, and never use it in my own code. `%y` is perhaps most needed for dealing with legacy files born at a time when the year 2000 was the stuff of science fiction and 4 bytes for storing a year as text seemed extravagant. Get the thing parsed in and then format it unambiguously with `%Y`. :-) – Howard Hinnant Mar 21 '17 at 15:37
  • Thank you, it would be interesting to obtain ISO 8601 compliance. By the way, `"19"` should parse with `"%C"` format :P – Valeriop84 Mar 21 '17 at 15:47
  • @Valeriop84: You can now parse and format `"19"` with `"%y"`, and `"1912"` with `"%y%m"`. However `"%C"` is still not parsable in isolation. I don't have a dedicated type to parse it into. https://github.com/HowardHinnant/date/commit/3495c513a1a3bf92289024f692783ab657ed0538 – Howard Hinnant Mar 25 '17 at 21:50
  • 1
    Thank you, i really appreciate that. – Valeriop84 Mar 29 '17 at 21:50
-1

It's not a bug. You've run into an artifact of Y2K. The tm_year in field in the std::tm structure contains the number of years since 1900. So naturally whatever value for year you get will be added to 1900. Ancient tools like date actually do follow the POSIX spec and behave accordingly:

date -d'27 JUN 69' +%Y
1969
date -d'27 JUN 68' +%Y
2068
user7744704
  • 124
  • 1
  • 1
    You've missed that the format specifier was lowercase y, which should read 2 characters for the year (19), giving either 1919 or 2019, but it read 4 characters (1912) – Simon Callan Mar 21 '17 at 10:17