Новый ответ на старый вопрос:
Обоснование этого нового ответа: Существующие ответы либо не показывают алгоритмы для преобразования из наносекунд в год / месяц / день (например, они используют библиотеки со скрытым источником), или они используют итерацию в алгоритмах, которые они показывают.
Этот ответ не имеет итерации вообще.
Алгоритмы здесь и объяснены в мучительных деталях.Они также тестируются на корректность в течение +/- миллиона лет (намного больше, чем нужно).
Алгоритмы не учитывают високосные секунды.Если вам это нужно, это может быть сделано, но требуется поиск в таблице, и эта таблица растет со временем.
Алгоритмы дат работают только с единицами дней, а не с наносекундами.Чтобы преобразовать дни в наносекунды, умножьте на 86400*1000000000
(следя за тем, чтобы использовать 64-битную арифметику).Чтобы преобразовать наносекунды в дни, разделите на ту же сумму.Или, что еще лучше, используйте библиотеку C ++ 11 <chrono>
.
Есть три алгоритма даты из этой статьи, которые необходимы для ответа на этот вопрос.
1.
days_from_civil
:
// 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;
}
2.
civil_from_days
:
// 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);
}
3.
weekday_from_days
:
// Returns day of week in civil calendar [0, 6] -> [Sun, Sat]
// Preconditions: z is number of days since 1970-01-01 and is in the range:
// [numeric_limits<Int>::min(), numeric_limits<Int>::max()-4].
template <class Int>
constexpr
unsigned
weekday_from_days(Int z) noexcept
{
return static_cast<unsigned>(z >= -4 ? (z+4) % 7 : (z+5) % 7 + 6);
}
Эти алгоритмы написаны для C ++ 14,Если у вас есть C ++ 11, удалите constexpr
.Если у вас есть C ++ 98/03, удалите constexpr
, noexcept
и static_assert
s.
Обратите внимание на отсутствие итерации в любом из этих трех алгоритмов.
Их можно использовать так:
#include <iostream>
int
main()
{
int64_t z = days_from_civil(2015LL, 8, 22);
int64_t ns = z*86400*1000000000;
std::cout << ns << '\n';
const char* weekdays[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
unsigned wd = weekday_from_days(z);
int64_t y;
unsigned m, d;
std::tie(y, m, d) = civil_from_days(ns/86400/1000000000);
std::cout << y << '-' << m << '-' << d << ' ' << weekdays[wd] << '\n';
}
, который выводит:
1440201600000000000
2015-8-22 Sat
Алгоритмы находятся в свободном доступе.Используйте их как хотите.* * * * * * * * * * * * * * * * * * * * * * * * * * [10] * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * [кроссплатформенная, безопасная библиотека типов , если необходимо.
Если требуется поддержка часового пояса или високосной секунды, то существует библиотека часовых поясов , построенная поверх даты библиотека .
Обновление: различные локальные зоны в одном приложении
Узнайте, как преобразовать различные часовые пояса .
Обновление: Есть ли какие-либо ошибки в игнорировании високосных секунд при выполнении вычислений даты таким способом?
Это хороший вопрос из комментариев ниже.
Ответ: Есть некоторые подводные камни.И есть некоторые преимущества.Хорошо знать, что они оба.
Почти каждый источник времени ОС основан на Unix Time . Unix Time - это отсчет времени с 1970-01-01 без учета високосных секунд.Это включает в себя такие функции, как C time(nullptr)
и C ++ std::chrono::system_clock::now()
, а также POSIX gettimeofday
и clock_gettime
.Это не факт, указанный в стандарте (за исключением того, что определено в POSIX), но это де-факто стандарт.
Так что, если ваш источник секунд (наносекунды, что угодно) пренебрегает високосными секундами, это точноисправлено, чтобы игнорировать дополнительные секунды при преобразовании в типы полей, такие как {year, month, day, hours, minutes, seconds, nanoseconds}
.Фактически, чтобы учитывать високосные секунды в таком контексте, на самом деле вводит ошибок.
Так что хорошо знать ваш источник времени и особенно знать, если он также пренебрегает високосными секундами.как Unix Time делает.
Если ваш источник времени не пренебрегает високосными секундами, вы можете все же получить правильный ответ довторой.Вам просто нужно знать набор високосных секунд, которые были вставлены. Вот текущий список .
Например, если вы получили количество секунд с 1970-01-01 00:00:00 UTC, которое включает високосных секунд ивы знаете, что это представляет «сейчас» (который в настоящее время 2016-09-26), текущее количество високосных секунд, вставленных между текущим и 1970-01-01, равно 26. Таким образом, вы можете вычесть 26 из своего количества и затем следуйте этим алгоритмам, получая точный результат.
Эта библиотека может автоматизировать вычисления с учетом високосных секунд.Например, чтобы получить количество секунд между 2016-09-26 00:00:00 UTC и 1970-01-01 00:00:00 UTC , включая високосных секунд, вы можете сделать это:
#include "date/tz.h"
#include <iostream>
int
main()
{
using namespace date;
auto now = clock_cast<utc_clock>(sys_days{2016_y/September/26});
auto then = clock_cast<utc_clock>(sys_days{1970_y/January/1});
std::cout << now - then << '\n';
}
, который выдает:
1474848026s
Пренебрежение високосными секундами ( Unix Time ) выглядит так:
#include "date/date.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std::chrono_literals;
auto now = sys_days{2016_y/September/26} + 0s;
auto then = sys_days{1970_y/January/1};
std::cout << now - then << '\n';
}
, который выдает:
1474848000s
Для разницы 26s
.
В предстоящие новогодние праздники (2017-01-01) мы добавим 27 th високосную секунду.
Между 1958-01-01 и 1970-01-01 было вставлено 10 «високосных секунд», но в единицах, меньших секунды, а не только в конце декабря или июня. Документация о том, как именнобыло затрачено много времени, и именно тогда, когда оно было отрывочным, и я не смог отыскать надежный источник.
Службы атомного отсчета времени начали экспериментально в 1955 году, и первый основанный на атомах международный стандарт времени TAI имеетЭпоха 1958-01-01 00:00:00 по Гринвичу (что сейчас UTC).До этого лучшими были кварцевые часы, которые не были достаточно точными, чтобы беспокоиться о високосных секундах.