Risposta aggiornata con algoritmi migliori, collegamento alla descrizione dettagliata degli algoritmi e conversione completa in std::tm
.
Vorrei stampare o valori di anno/mese/giorno di estratto. C'è un modo semplice per convertire da time_point a tm (preferibilmente senza boost)?
La prima cosa da notare è che std::chrono::time_point
è basato su modelli non solo sulla duration
, ma anche sull'orologio. L'orologio implica un'epoca. E diversi orologi possono avere epoche diverse.
Ad esempio, sul mio sistema, std::chrono::high_resolution_clock
e std::chrono::steady_clock
hanno un'epoca di: ogni volta che il computer si avvia.Se non si conosce l'ora di avvio del computer, non c'è modo di convertire tale time_point
in qualsiasi sistema di calendario.
Detto questo, si erano probabilmente parlando solo di std::chrono::system_clock::time_point
, in quanto questo time_point
, e solo questo time_point
, è necessario per avere un rapporto deterministico con la (gregoriano) calendario civile.
Come risulta, ogni implementazione di std::chrono::system_clock
di cui sono a conoscenza sta utilizzando unix time. Questo ha un'epoca di Capodanno 1970 trascurando secondi bisestili.
Questo non è garantito dallo standard. Tuttavia si può approfittare di questo fatto se si vuole con le seguenti formule disponibili all'indirizzo:
chrono-Compatible Low-Level Date Algorithms
Prima di tutto, di avvertimento, sto usando l'ultimo C++ 1A progetto, che include fantastici nuovi strumenti constexpr
. Se hai bisogno di annullare alcuni degli attributi constexpr
per il tuo compilatore, fallo semplicemente.
Dato gli algoritmi trovate al link qui sopra, è possibile in grado di convertire un std::chrono::time_point<std::chrono::system_clock, Duration>
ad un std::tm
, senza utilizzare time_t
con la seguente funzione:
template <class Duration>
std::tm
make_utc_tm(std::chrono::time_point<std::chrono::system_clock, Duration> tp)
{
using namespace std;
using namespace std::chrono;
typedef duration<int, ratio_multiply<hours::period, ratio<24>>> days;
// t is time duration since 1970-01-01
Duration t = tp.time_since_epoch();
// d is days since 1970-01-01
days d = round_down<days>(t);
// t is now time duration since midnight of day d
t -= d;
// break d down into year/month/day
int year;
unsigned month;
unsigned day;
std::tie(year, month, day) = civil_from_days(d.count());
// start filling in the tm with calendar info
std::tm tm = {0};
tm.tm_year = year - 1900;
tm.tm_mon = month - 1;
tm.tm_mday = day;
tm.tm_wday = weekday_from_days(d.count());
tm.tm_yday = d.count() - days_from_civil(year, 1, 1);
// Fill in the time
tm.tm_hour = duration_cast<hours>(t).count();
t -= hours(tm.tm_hour);
tm.tm_min = duration_cast<minutes>(t).count();
t -= minutes(tm.tm_min);
tm.tm_sec = duration_cast<seconds>(t).count();
return tm;
}
anche notare che il std::chrono::system_clock::time_point
su tutte le implementazioni esistenti è una durata nel fuso orario UTC (trascurando secondi intercalari). Se si desidera convertire time_point
utilizzando un altro fuso orario, sarà necessario aggiungere/sottrarre l'offset di durata del fuso orario a std::chrono::system_clock::time_point
prima di convertirlo in una precisione di days
. Se si desidera inoltre tenere conto dei secondi bisestili, regolare il numero appropriato di secondi prima del troncamento a days
utilizzando this table e sapere che l'ora di unix è allineata con UTC ora.
Questa funzione può essere verificata con:
#include <iostream>
#include <iomanip>
void
print_tm(const std::tm& tm)
{
using namespace std;
cout << tm.tm_year+1900;
char fill = cout.fill();
cout << setfill('0');
cout << '-' << setw(2) << tm.tm_mon+1;
cout << '-' << setw(2) << tm.tm_mday;
cout << ' ';
switch (tm.tm_wday)
{
case 0:
cout << "Sun";
break;
case 1:
cout << "Mon";
break;
case 2:
cout << "Tue";
break;
case 3:
cout << "Wed";
break;
case 4:
cout << "Thu";
break;
case 5:
cout << "Fri";
break;
case 6:
cout << "Sat";
break;
}
cout << ' ';
cout << ' ' << setw(2) << tm.tm_hour;
cout << ':' << setw(2) << tm.tm_min;
cout << ':' << setw(2) << tm.tm_sec << " UTC.";
cout << setfill(fill);
cout << " This is " << tm.tm_yday << " days since Jan 1\n";
}
int
main()
{
print_tm(make_utc_tm(std::chrono::system_clock::now()));
}
che per me attualmente stampa i:
2013-09-15 18:16:50 UTC Sun. Si tratta di 257 giorni dal gennaio 1
In caso chrono-Compatible Low-Level Date Algorithms va offline, o viene spostato, qui ci sono gli algoritmi utilizzati in make_utc_tm
. Ci sono spiegazioni approfondite di questi algoritmi al link sopra. Sono ben testati e hanno una gamma straordinariamente ampia di validità.
// Returns number of days since civil 1970-01-01. Negative values indicate
// days prior to 1970-01-01.
// Preconditions: y-m-d represents a date in the civil (Gregorian) calendar
// m is in [1, 12]
// d is in [1, last_day_of_month(y, m)]
// y is "approximately" in
// [numeric_limits<Int>::min()/366, numeric_limits<Int>::max()/366]
// Exact range of validity is:
// [civil_from_days(numeric_limits<Int>::min()),
// civil_from_days(numeric_limits<Int>::max()-719468)]
template <class Int>
constexpr
Int
days_from_civil(Int y, unsigned m, unsigned d) noexcept
{
static_assert(std::numeric_limits<unsigned>::digits >= 18,
"This algorithm has not been ported to a 16 bit unsigned integer");
static_assert(std::numeric_limits<Int>::digits >= 20,
"This algorithm has not been ported to a 16 bit signed integer");
y -= m <= 2;
const Int era = (y >= 0 ? y : y-399)/400;
const unsigned yoe = static_cast<unsigned>(y - era * 400); // [0, 399]
const unsigned doy = (153*(m + (m > 2 ? -3 : 9)) + 2)/5 + d-1; // [0, 365]
const unsigned doe = yoe * 365 + yoe/4 - yoe/100 + doy; // [0, 146096]
return era * 146097 + static_cast<Int>(doe) - 719468;
}
// Returns year/month/day triple in civil calendar
// Preconditions: z is number of days since 1970-01-01 and is in the range:
// [numeric_limits<Int>::min(), numeric_limits<Int>::max()-719468].
template <class Int>
constexpr
std::tuple<Int, unsigned, unsigned>
civil_from_days(Int z) noexcept
{
static_assert(std::numeric_limits<unsigned>::digits >= 18,
"This algorithm has not been ported to a 16 bit unsigned integer");
static_assert(std::numeric_limits<Int>::digits >= 20,
"This algorithm has not been ported to a 16 bit signed integer");
z += 719468;
const Int era = (z >= 0 ? z : z - 146096)/146097;
const unsigned doe = static_cast<unsigned>(z - era * 146097); // [0, 146096]
const unsigned yoe = (doe - doe/1460 + doe/36524 - doe/146096)/365; // [0, 399]
const Int y = static_cast<Int>(yoe) + era * 400;
const unsigned doy = doe - (365*yoe + yoe/4 - yoe/100); // [0, 365]
const unsigned mp = (5*doy + 2)/153; // [0, 11]
const unsigned d = doy - (153*mp+2)/5 + 1; // [1, 31]
const unsigned m = mp + (mp < 10 ? 3 : -9); // [1, 12]
return std::tuple<Int, unsigned, unsigned>(y + (m <= 2), m, d);
}
template <class Int>
constexpr
unsigned
weekday_from_days(Int z) noexcept
{
return static_cast<unsigned>(z >= -4 ? (z+4) % 7 : (z+5) % 7 + 6);
}
template <class To, class Rep, class Period>
To
round_down(const std::chrono::duration<Rep, Period>& d)
{
To t = std::chrono::duration_cast<To>(d);
if (t > d)
--t;
return t;
}
Aggiornamento
Più di recente ho avvolto gli algoritmi di cui sopra fino in una libreria data/ora liberamente disponibile documented and available here. Questa libreria semplifica l'estrazione di un anno/mese/giorno da std::system_clock::time_point
e persino di ore: minuti: secondi: secondi frazionari. E tutto senza passare per time_t
.
Ecco un semplice programma che utilizza la libreria di testa-solo al di sopra di stampare la data e l'ora corrente nel fuso orario UTC, alla precisione di qualsiasi system_clock::time_point
offerte (in questo caso microsecondi):
#include "date.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std;
using namespace std::chrono;
auto const now = system_clock::now();
auto const dp = time_point_cast<days>(now);
auto const date = year_month_day(dp);
auto const time = make_time(now-dp);
cout << date << ' ' << time << " UTC\n";
}
che ha appena uscita per me:
2015-05-19 15:03:47.754002 UTC
Questa libreria si trasforma in modo efficace std::chrono::system_clock::time_point
in un tipo di data-ora facile da usare.
Una domanda correlata [Cosa dovremmo fare per preparare il 2038?] (Http://stackoverflow.com/q/36239/341970) – Ali
Un secondo problema con il metodo di passare attraverso 'time_t' è che tutto il le funzioni standard per la conversione di 'time_t' in' tm' non sono thread-safe. –