I have seconds since Jan 1 1970 00:00 as an int64 in nanoseconds and I'm trying to convert it into month/day/year/day of week.
It's easy to do this iteratively, I have that working but I want to do it formulaically. I'm looking for the actual math.
New answer for old question:
Rationale for this new answer: The existing answers either do not show the algorithms for the conversion from nanoseconds to year/month/day (e.g. they use libraries with the source hidden), or they use iteration in the algorithms they do show.
This answer has no iteration whatsoever.
The algorithms are here, and explained in excruciating detail. They are also unit tested for correctness over a span of +/- a million years (way more than you need).
The algorithms don't count leap seconds. If you need that, it can be done, but requires a table lookup, and that table grows with time.
The date algorithms deal only with units of days, and not nanoseconds. To convert days to nanoseconds, multiply by 86400*1000000000 (taking care to ensure you're using 64 bit arithmetic). To convert nanoseconds to days, divide by the same amount. Or better yet, use the C++11 <chrono> library.
There are three date algorithms from this paper that are needed to answer this question.
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);
}
These algorithms are written for C++14. If you have C++11, remove the constexpr. If you have C++98/03, remove the constexpr, the noexcept, and the static_asserts.
Note the lack of iteration in any of these three algorithms.
They can be used like this:
#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';
}
which outputs:
1440201600000000000
2015-8-22 Sat
The algorithms are in the public domain. Use them however you want. The date algorithms paper has several more useful date algorithms if needed (e.g. weekday_difference is both remarkably simple and remarkably useful).
These algorithms are wrapped up in an open source, cross platform, type-safe date library if needed.
If timezone or leap second support is needed, there exists a timezone library built on top of the date library.
Update: Different local zones in same app
See how to convert among different time zones.
Update: Are there any pitfalls to ignoring leap seconds when doing date calculations in this manner?
This is a good question from the comments below.
Answer: There are some pitfalls. And there are some benefits. It is good to know what they both are.
Almost every source of time from an OS is based on Unix Time. Unix Time is a count of time since 1970-01-01 excluding leap seconds. This includes functions like the C time(nullptr) and the C++ std::chrono::system_clock::now(), as well as the POSIX gettimeofday and clock_gettime. This is not a fact specified by the standard (except it is specified by POSIX), but it is the de facto standard.
So if your source of seconds (nanoseconds, whatever) neglects leap seconds, it is exactly correct to ignore leap seconds when converting to field types such as {year, month, day, hours, minutes, seconds, nanoseconds}. In fact to take leap seconds into account in such a context would actually introduce errors.
So it is good to know your source of time, and especially to know if it also neglects leap seconds as Unix Time does.
If your source of time does not neglect leap seconds, you can still get the correct answer down to the second. You just need to know the set of leap seconds that have been inserted. Here is the current list.
For example if you get a count of seconds since 1970-01-01 00:00:00 UTC which includes leap seconds and you know that this represents "now" (which is currently 2016-09-26), the current number of leap seconds inserted between now and 1970-01-01 is 26. So you could subtract 26 from your count, and then follow these algorithms, getting the exact result.
This library can automate leap-second-aware computations for you. For example to get the number of seconds between 2016-09-26 00:00:00 UTC and 1970-01-01 00:00:00 UTC including leap seconds, you could do this:
#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';
}
which outputs:
1474848026s
Neglecting leap seconds (Unix Time) looks like:
#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';
}
which outputs:
1474848000s
For a difference of 26s.
This upcoming New Years (2017-01-01) we will insert the 27th leap second.
Between 1958-01-01 and 1970-01-01 10 "leap seconds" were inserted, but in units smaller than a second, and not just at the end of Dec or Jun. Documentation on exactly how much time was inserted and exactly when is sketchy, and I have not been able to track down a reliable source.
Atomic time keeping services began experimentally in 1955, and the first atomic-based international time standard TAI has an epoch of 1958-01-01 00:00:00 GMT (what is now UTC). Prior to that the best we had was quartz-based clocks which were not accurate enough to worry about leap seconds.
The Single Unix Specification gives a formula for Seconds since the Epoch:
A value that approximates the number of seconds that have elapsed
since the Epoch. A Coordinated Universal Time name (specified in terms
of seconds (tm_sec), minutes (tm_min), hours (tm_hour), days since
January 1 of the year (tm_yday), and calendar year minus 1900
(tm_year)) is related to a time represented as seconds since the
Epoch, according to the expression below.
If the year is <1970 or the value is negative, the relationship is
undefined. If the year is >=1970 and the value is non-negative, the
value is related to a Coordinated Universal Time name according to the
C-language expression, where tm_sec, tm_min, tm_hour, tm_yday, and
tm_year are all integer types:
tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
(tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400
The relationship between the actual time of day and the current value
for seconds since the Epoch is unspecified.
How any changes to the value of seconds since the Epoch are made to
align to a desired relationship with the current actual time is
implementation-defined. As represented in seconds since the Epoch,
each and every day shall be accounted for by exactly 86400 seconds.
Note:
The last three terms of the expression add in a day for each year that follows a leap year starting with the first leap year since the
Epoch. The first term adds a day every 4 years starting in 1973, the
second subtracts a day back out every 100 years starting in 2001, and
the third adds a day back in every 400 years starting in 2001. The
divisions in the formula are integer divisions; that is, the remainder
is discarded leaving only the integer quotient.
You'll need to convert month and day of month to tm_yday to use this formula and that too should be done taking into account leap years. The rest in the formula is trivial.
Try to figure out from this how to get back date and time from seconds.
EDIT:
I've implemented a convertor in integer arithmetic in this answer.
See a test run at ideone.
Depends on which time you want gmtime or localtime then just read the struct_tm
This code works...
Usage:
uint32_t getSecsSinceEpoch(1970, month, day, years_since_epoch, hour, minute, second);
Example:
timestamp = getSecsSinceEpoch(1970, 6, 12, (2014 - 1970), 15, 29, 0)
Returns: 1402586940
You can verify at www.epochconverter.com.
Took about 20 mins to write it and most of that was spent arguing with a friend as to whether I should include leap-seconds, nano-seconds, etc. Blech.
Have fun...
Dr. Bryan Wilcutt
#define DAYSPERWEEK (7)
#define DAYSPERNORMYEAR (365U)
#define DAYSPERLEAPYEAR (366U)
#define SECSPERDAY (86400UL) /* == ( 24 * 60 * 60) */
#define SECSPERHOUR (3600UL) /* == ( 60 * 60) */
#define SECSPERMIN (60UL) /* == ( 60) */
#define LEAPYEAR(year) (!((year) % 4) && (((year) % 100) || !((year) % 400)))
const int _ytab[2][12] = {
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};
/****************************************************
* Class:Function : getSecsSomceEpoch
* Input : uint16_t epoch date (ie, 1970)
* Input : uint8 ptr to returned month
* Input : uint8 ptr to returned day
* Input : uint8 ptr to returned years since Epoch
* Input : uint8 ptr to returned hour
* Input : uint8 ptr to returned minute
* Input : uint8 ptr to returned seconds
* Output : uint32_t Seconds between Epoch year and timestamp
* Behavior :
*
* Converts MM/DD/YY HH:MM:SS to actual seconds since epoch.
* Epoch year is assumed at Jan 1, 00:00:01am.
****************************************************/
uint32_t getSecsSinceEpoch(uint16_t epoch, uint8_t month, uint8_t day, uint8_t years, uint8_t hour, uint8_t minute, uint8_t second)
{
unsigned long secs = 0;
int countleap = 0;
int i;
int dayspermonth;
secs = years * (SECSPERDAY * 365);
for (i = 0; i < (years - 1); i++)
{
if (LEAPYEAR((epoch + i)))
countleap++;
}
secs += (countleap * SECSPERDAY);
secs += second;
secs += (hour * SECSPERHOUR);
secs += (minute * SECSPERMIN);
secs += ((day - 1) * SECSPERDAY);
if (month > 1)
{
dayspermonth = 0;
if (LEAPYEAR((epoch + years))) // Only counts when we're on leap day or past it
{
if (month > 2)
{
dayspermonth = 1;
} else if (month == 2 && day >= 29) {
dayspermonth = 1;
}
}
for (i = 0; i < month - 1; i++)
{
secs += (_ytab[dayspermonth][i] * SECSPERDAY);
}
}
return secs;
}
bool FloatToTime(float seconds_since_epoch, bool local_time, struct tm *timest)
{
struct tm *ret;
time_t t=(time_t) seconds_since_epoch;
if (local_time) ret=localtime(&t);
else ret=gmtime(&t);
if(ret==NULL) return false;
memcpy(timest, ret, sizeof(struct tm));
return true;
}
Pass it the seconds as the first parameter. The second parameter should be true for local time, false for GMT. The third parameter is a pointer to a structure to hold the response.
The return structures are (from the man page):
tm_sec: The number of seconds after the minute, normally in the range 0 to
59, but can be up to 60 to allow for leap seconds.
tm_min: The number of minutes after the hour, in the range 0 to 59.
tm_hour: The number of hours past midnight, in the range 0 to 23.
tm_mday: The day of the month, in the range 1 to 31.
tm_mon: The number of months since January, in the range 0 to 11.
tm_year: The number of years since 1900.
tm_wday: The number of days since Sunday, in the range 0 to 6.
tm_yday: The number of days since January 1, in the range 0 to 365.
tm_isdst: A flag that indicates whether daylight saving time is in effect
at the time described. The value is positive if daylight saving
time is in effect, zero if it is not, and negative if the
information is not available.
First of all, do not store your seconds as a float. If you need micro/nanoseconds, store them separately. You're going to need integers to do these calculations.
It depends on your time zone (DST rules, leap years, leap seconds), but I would say first get the number of days by integer dividing by 86400. Then find out what's left over, by modulo dividing by 86400. Now you can figure out how many years have passed by first integer dividing the number of days by 365, and then subtracting the number of leap days from the remaining days (calculated by modulo dividing the number of days by 365). You'll also want to subtract the number of leap seconds from the number of remaining seconds (already calculated). If that subtraction drives those numbers below zero, then subtract from the next biggest denomination. Then you can calculate the day of month using explicit logic for your calendar. Make sure to add an hour (or whatever the DST offset is) if you land in DST.
Personally, I would just use Boost.Date_Time, since it does all this and more (probably with fewer mistakes than you or I would make in the first few iterations), but I figured I'd take a shot at your question...
BEFORE
for (i = 0; i < (years - 1); i++)
{
if (LEAPYEAR((epoch + i)))
countleap++;
}
LATER:
for (i = 0; i < years; i++)
{
if (LEAPYEAR((epoch + i)))
countleap++;
}
After the correction the code worked for me.
I needed to implement conversion to Unix time at a low-end 8-bit MCU without HW multiplier. Below is the C# code that requires only a general 8-bit multiplication and a division by constant values 4 and 100. Both on 32-bit (long) operand. The C# code can be easily ported to the final framework. It gives the same result as DateTimeOffset.ToUnixTimeSeconds() from .NET.
static long UnixTime ( int sec, int min, int hour, int day, int month, int year )
{
// Cumulative days for each previous month of the year
int[] mdays = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
// Year is to be relative to the epoch start
year -= 1970;
// Compensation of the non-leap years
int minusYear = 0;
// Detect potential lead day (February 29th) in this year?
if ( month >= 3 )
{
// Then add this year into "sum of leap days" computation
year++;
// Compute one year less in the non-leap years sum
minusYear = 1;
}
return
// + Seconds from computed minutes
60 * (
// + Minutes from computed hours
60 * (
// + Hours from computed days
24 * (
// + Day (zero index)
day - 1
// + days in previous months (leap day not included)
+ mdays[month - 1]
// + days for each year divisible by 4 (starting from 1973)
+ ( ( year + 1 ) / 4 )
// - days for each year divisible by 100 (starting from 2001)
- ( ( year + 69 ) / 100 )
// + days for each year divisible by 400 (starting from 2001)
+ ( ( year + 369 ) / 100 / 4 )
// + days for each year (as all are non-leap years) from 1970 (minus this year if potential leap day taken into account)
+ ( 5 * 73 /*=365*/ ) * ( year - minusYear )
// + Hours
) + hour
// + Minutes
) + min
// + Seconds
) + sec;
}
Hope it helps.
Edited:
Below is the optimized code for 8-bit PIC MCU and CC5X compiler.
uns32 unixTime;
...
// Test data returning 0xFfFfFfFf UnixTime
uns8 year = 2106 - 1970;
uns8 month = 2;
uns8 day = 7;
uns8 hour = 6;
uns8 min = 28;
uns8 sec = 15;
// See original C# code below
//### Compute days
// ( 5 * 73 /*=365*/ ) * year
unixTime = year;
mulUnixTime( 5 );
mulUnixTime( 73 );
// if ( month >= 3 ) year++;
if ( month > 3 )
year++;
// if ( year > 130 ) => minus 1 total days ( year-=4 makes a result of the next division by 4 less by 1)
if ( year > 130 )
year -= 4;
// + ( ( year + 1 ) / 4 )
addUnixTime( ( year + 1 ) / 4 );
// + mdays[month - 1]
addUnixTime( daysInMonths( month ) );
// + day - 1
addUnixTime( day - 1 );
//### Compute hours
// Hours from computed days
mulUnixTime( 24 );
// + Hours
addUnixTime( hour );
//### Compute minutes
// Minutes from computed hours
mulUnixTime( 60 );
// + Minutes
addUnixTime( min );
//### Compute seconds
// Seconds from computed minutes
mulUnixTime( 60 );
// + Seconds
addUnixTime( sec );
...
void mulUnixTime( uns8 mul )
{
unixTime *= mul;
}
void addUnixTime( uns8 add )
{
unixTime += add;
}
uns8 daysInMonths( uns8 month # W )
{
skip( month );
#pragma computedGoto 1
return 0xFF;// Dummy value for month 0
return 0; // January
return 31; // February
return 59; // ...
return 90;
return 120;
return 151;
return 181;
return 212;
return 243;
return 273;
return 304; // ...
return 334; // December
#pragma computedGoto 0
}
/*
static long UnixTime ( int sec, int min, int hour, int day, int month, int year )
{
// Cumulative days for each previous month of the year
int[] mdays = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
// Year is to be relative to the epoch start
year -= 1970;
// Compensation of the non-leap years
int minusYear = 0;
// Detect potential lead day (February 29th) in this year?
if ( month >= 3 )
{
// Then add this year into "sum of leap days" computation
year++;
// Compute one year less in the non-leap years sum
minusYear = 1;
}
return
// + Seconds from computed minutes
60 * (
// + Minutes from computed hours
60 * (
// + Hours from computed days
24L * (
// + Day (zero index)
day - 1
// + days in previous months (leap day not included)
+ mdays[month - 1]
// + days for each year divisible by 4 (starting from 1973)
+ ( ( year + 1 ) / 4 )
// - days after year 2000
- ( ( year > 130 ) ? 1 : 0 )
// + days for each year (as all are non-leap years) from 1970 (minus this year if potential leap day taken into account)
+ ( 5 * 73 ) * ( year - minusYear )
// + Hours
) + hour
// + Minutes
) + min
// + Seconds
) + sec;
}
*/
Related
I'm writing a Windows DLL in mostly std C++ (VS2010), which does not use MFC/ATL.
A parent module does use MFC and passes a COleDateTime.m_dt to my DLL, which arrives as a double. I believe this is an OLE Automation Date, also known as OADate.
I want to convert this to any type of standard struct (tm...) that has days, hours, etc without pulling MFC, OLE, etc into my DLL.
This has been asked before (Convert Date/Time (as Double) to struct* tm in C++) however, the answer is always using VariantTimeToSystemTime(), which misses the point of that question - not using MFC / OLE, etc.
VariantTimeToSystemTime's requirements are:
Header - OleAuto.h
Library - OleAut32.lib
DLL - OleAut32.dll
My DLL has basically no dependencies at the moment, so I would prefer not to pull OleAut32.dll in for this one conversion.
The best thing I've found so far has been this C# mono code, which I may convert to C++.
I have 2 solutions, the first is working with a function that implements gmtime_r so that this solution will don't use any standard functions. The second solution is using the standard function gmtime_r.
1. First solution: Own implementation of gmtime_r (01-Jan-1601 to 31-Dec-9999):
It will work for dates between 01-Jan-1601 and 31-Dec-9999. I've implemented a fromOADate function which uses the SecondsSinceEpochToDateTime function from this answer on SO wich converts seconds before or after 01-Jan-1970 to a tm structure but works only from 01-Jan-1601 on.
I changed the function from that answer to work also with 32 bit by adding one ULL suffix. That requires that the long long types are 64 bit wide, if that's not the case this solution will not work.
If you need dates before year 1601 you could change the SecondsSinceEpochToDateTime as it is well documentated.
To test different values this online conversion is very nice which also supports unix timestamp and the OADate type.
Full working code and example on ideone:
#include <iostream>
#include <ctime>
#include <cstring>
struct tm* SecondsSinceEpochToDateTime(struct tm* pTm, uint64_t SecondsSinceEpoch)
{
uint64_t sec;
unsigned int quadricentennials, centennials, quadrennials, annuals/*1-ennial?*/;
unsigned int year, leap;
unsigned int yday, hour, min;
unsigned int month, mday, wday;
static const unsigned int daysSinceJan1st[2][13]=
{
{0,31,59,90,120,151,181,212,243,273,304,334,365}, // 365 days, non-leap
{0,31,60,91,121,152,182,213,244,274,305,335,366} // 366 days, leap
};
/*
400 years:
1st hundred, starting immediately after a leap year that's a multiple of 400:
n n n l \
n n n l } 24 times
... /
n n n l /
n n n n
2nd hundred:
n n n l \
n n n l } 24 times
... /
n n n l /
n n n n
3rd hundred:
n n n l \
n n n l } 24 times
... /
n n n l /
n n n n
4th hundred:
n n n l \
n n n l } 24 times
... /
n n n l /
n n n L <- 97'th leap year every 400 years
*/
// Re-bias from 1970 to 1601:
// 1970 - 1601 = 369 = 3*100 + 17*4 + 1 years (incl. 89 leap days) =
// (3*100*(365+24/100) + 17*4*(365+1/4) + 1*365)*24*3600 seconds
sec = SecondsSinceEpoch + 11644473600ULL;
wday = (uint)((sec / 86400 + 1) % 7); // day of week
// Remove multiples of 400 years (incl. 97 leap days)
quadricentennials = (uint)(sec / 12622780800ULL); // 400*365.2425*24*3600
sec %= 12622780800ULL;
// Remove multiples of 100 years (incl. 24 leap days), can't be more than 3
// (because multiples of 4*100=400 years (incl. leap days) have been removed)
centennials = (uint)(sec / 3155673600ULL); // 100*(365+24/100)*24*3600
if (centennials > 3)
{
centennials = 3;
}
sec -= centennials * 3155673600ULL;
// Remove multiples of 4 years (incl. 1 leap day), can't be more than 24
// (because multiples of 25*4=100 years (incl. leap days) have been removed)
quadrennials = (uint)(sec / 126230400); // 4*(365+1/4)*24*3600
if (quadrennials > 24)
{
quadrennials = 24;
}
sec -= quadrennials * 126230400ULL;
// Remove multiples of years (incl. 0 leap days), can't be more than 3
// (because multiples of 4 years (incl. leap days) have been removed)
annuals = (uint)(sec / 31536000); // 365*24*3600
if (annuals > 3)
{
annuals = 3;
}
sec -= annuals * 31536000ULL;
// Calculate the year and find out if it's leap
year = 1601 + quadricentennials * 400 + centennials * 100 + quadrennials * 4 + annuals;
leap = !(year % 4) && (year % 100 || !(year % 400));
// Calculate the day of the year and the time
yday = sec / 86400;
sec %= 86400;
hour = sec / 3600;
sec %= 3600;
min = sec / 60;
sec %= 60;
// Calculate the month
for (mday = month = 1; month < 13; month++)
{
if (yday < daysSinceJan1st[leap][month])
{
mday += yday - daysSinceJan1st[leap][month - 1];
break;
}
}
// Fill in C's "struct tm"
memset(pTm, 0, sizeof(*pTm));
pTm->tm_sec = sec; // [0,59]
pTm->tm_min = min; // [0,59]
pTm->tm_hour = hour; // [0,23]
pTm->tm_mday = mday; // [1,31] (day of month)
pTm->tm_mon = month - 1; // [0,11] (month)
pTm->tm_year = year - 1900; // 70+ (year since 1900)
pTm->tm_wday = wday; // [0,6] (day since Sunday AKA day of week)
pTm->tm_yday = yday; // [0,365] (day since January 1st AKA day of year)
pTm->tm_isdst = -1; // daylight saving time flag
return pTm;
}
struct tm* fromOADate(struct tm* p_Tm, double p_OADate)
{
static const int64_t OA_UnixTimestamp = -2209161600; /* 30-Dec-1899 */
if (!( -109205 <= p_OADate /* 01-Jan-1601 */
&& p_OADate <= 2958465)) /* 31-Dec-9999 */
{
throw std::string("OADate must be between 109205 and 2958465!");
}
int64_t OADatePassedDays = p_OADate;
double OADateDayTime = p_OADate - OADatePassedDays;
int64_t OADateSeconds = OA_UnixTimestamp
+ OADatePassedDays * 24LL * 3600LL
+ OADateDayTime * 24.0 * 3600.0;
return SecondsSinceEpochToDateTime(p_Tm, OADateSeconds);
}
int main()
{
struct tm timeVal;
std::cout << asctime(fromOADate(&timeVal, -109205)); /* 01-Jan-1601 00:00:00 */
std::cout << asctime(fromOADate(&timeVal, 0)); /* 30-Dec-1899 00:00:00 */
std::cout << asctime(fromOADate(&timeVal, 25569)); /* 01-Jan-1970 00:00:00 */
std::cout << asctime(fromOADate(&timeVal, 50424.134803241)); /* 19-Jan-2038 03:14:07 */
std::cout << asctime(fromOADate(&timeVal, 2958465)); /* 31-Dec-9999 00:00:00 */
return 0;
}
2. Second solution: Using gmtime_r (01-Jan-1970 to 19-Jan-2038/31-Dec-9999 (32/64 bit)):
As already said this solution has not that wide range as the variant from above but just uses a standard function (full working example at ideone):
#include <iostream>
#include <ctime>
struct tm* fromOADate(struct tm* p_Tm, double p_OADate)
{
static const int64_t OA_UnixTimestamp = -2209161600; /* 30-Dec-1899 */
if (!( 25569 <= p_OADate /* 01-Jan-1970 00:00:00 */
&& p_OADate <= 2958465)) /* 31-Dec-9999 00:00:00 */
{
throw std::string("OADate must be between 25569 and 2958465!");
}
time_t OADatePassedDays = p_OADate;
double OADateDayTime = p_OADate - OADatePassedDays;
time_t OADateSeconds = OA_UnixTimestamp
+ OADatePassedDays * 24LL * 3600LL
+ OADateDayTime * 24.0 * 3600.0;
/* date was greater than 19-Jan-2038 and build is 32 bit */
if (0 > OADateSeconds)
{
throw std::string("OADate must be between 25569 and 50424.134803241!");
}
return gmtime_r(&OADateSeconds, p_Tm);
}
int main()
{
struct tm timeVal;
std::cout << asctime(fromOADate(&timeVal, 25569)); /* 01-Jan-1970 00:00:00 */
std::cout << asctime(fromOADate(&timeVal, 50424.134803241)); /* 19-Jan-2038 03:14:07 */
return 0;
}
This question already has answers here:
How to get current time and date in Android
(42 answers)
Closed 6 years ago.
#include "stdafx.h"
#include <iostream>
#include <ctime>
using namespace std;
int main()
{
time_t now = time(NULL);
int sec = now % 60;
int min = ((now - sec) / 60) % 60;
int hour = ((((now - sec) / 60) - min) / 60) % 24;
int day = ((((((now - sec) / 60) - min) / 60) - hour) / 24) % 30;
int month = ((((((((now - sec) / 60) - min) / 60) - hour) / 24) - day) / 30) % 12;
int year = ((((((((((now - sec) / 60) - min) / 60) - hour) / 24) - day) / 30) - month) / 12) + 1970;
cout << hour << ":" << min << ":"<< ":" << day << ":" << month << ":" << year<<endl;
}
I can't understand why month output 10 instead of 3 ???
(I know there is c_time function but i am learning and making my own one)
time() function returns a number of seconds since January 1st, 1970.
Converting this into a date is much more complex than you think:
It starts with calculating the seconds: taking the number %60 should in principle tell the seconds in the current minute. That seems obvious. But this is not exact: it doesn't take into account the 25 leap seconds that took place since 1970.
minutes, hours would be ok if you take into account this first correction (and eventually taking into account daylight saving time, if this code is not ran in winter)
your calculation of the day of the month does not take into account the fact that not all the months are 30 days long, and it doesn't take into account the leap years.
And basically, the calculation of the months and the year would be affected by the impact of the wrong estimation of month.
Do yourself a favour and convert the date using a standard function like gmtime() or localtime() and accessing the fields of the struct tm
Converting seconds to days/hours/minutes is trivial. Converting days since 1970 to current date is not. You either should use library functions if you can or you may use formula provided for Julian day, just pay attention that 1/1/1970 is not 0 for Julian day.
PS subtracting reminder is completely unnecessary:
int min = ((now - sec) / 60) % 60;
should be:
int min = now / 60 % 60;
or even better:
int sec = now % 60;
now /= 60;
int min = now % 60;
now /= 60;
and so on.
It is extremely daunting task of converting 'seconds since epoch' to day/month/year/etc.
This is why library got you covered. There are gmtime_r() and localtime()_r designed specifically for that. Unfortunately, those functions are not standard, and the only standard ones are thread unsafe gmtime() and localtime().
In order that a device (with limited memory) is able to manage its own timezone and daylight savings, I'm trying to calculate daylight savings triggers for 85 time zones based on a simplified description of each timezone. I have access to minimal C and C++ libraries within the device. The format of the timezone (inc. DST) description for each time zone is as follows:
UTC - the base time and date from system clock
GMTOffsetMinutes - offset from GMT with DST inactive
DSTDeltaMinutes - modifier to above with DST active (as applicable to TZ)
DSTStartMonth - month in which DST becomes active
DSTStartNthOccurranceOfDay - the nth occurrence of the day name in month
DSTDayOfWeek - Sun = 0 through to Sat = 6
DSTStartHour - hour at which DST becomes active
DSTStartMinute - minute at which DST becomes active
and corresponding EndMonth, EndNth..., EndHour, EndMinute
I have found numerous examples going the other way, i.e. starting with the date, but they involve using the modulus, keeping the remainder and dropping the quotient hence I have been unable to transpose the formula to suit my needs.
I also tried to reuse the standard "Jan = 6, Feb = 2, Mar = 2, Apr = 5, May = 0, etc. modifier table and year modifiers from the "tell me what day the 25th of June, 2067 is?" party trick and developed the following algorithm.
Date = DayOfWeek + ((NthOccuranceOfDay - 1) x 7 ) - MonthCode - YearCode
This worked for the first 6 random test dates I selected but then I started to see dates for which it failed. Is it possible that the basic algorithm is sound but I'm missing a further modifier or maybe that I'm applying the modifiers incorrectly?
Is there another solution I could utilize?
Using this open source, cross platform date library, one can write:
#include "date.h"
#include <iostream>
int
main()
{
using namespace date;
year_month_day us_daylight_starts = sys_days(sun[2]/mar/2015);
year_month_day us_daylight_ends = sys_days(sun[1]/nov/2015);
std::cout << us_daylight_starts << '\n';
std::cout << us_daylight_ends << '\n';
}
which will output:
2015-03-08
2015-11-01
The formulas this library is based on are in the public domain and documented here.
The algorithms paper has very complete unit tests validating the date algorithms over a range of millions of years (a far larger range than is necessary).
Sometimes daylight savings rules are written in terms of the last weekday of a month. That is just as easily handled:
year_month_day ymd = sys_days(sun[last]/nov/2015);
std::cout << ymd << '\n'; // 2015-11-29
That formula will be off by one week (or even two weeks) if MonthCode + YearCode is greater than or equal to DayOfWeek, because in that case you will be counting NthOccurenceOfDay from a negative date.
As an alternative, with no tables, you can compute the day of week of the first of the month using, for example, Zeller's algorithm:
int NthOccurrence(int year, int month, int n, int dayOfWeek) {
// year is the current year (eg. 2015)
// month is the target month (January == 1...December == 12)
// Finds the date of the nth dayOfWeek (Sun == 0...Sat == 6)
// Adjust month and year
if (month < 3) { --year, month += 12; }
// The gregorian calendar is a 400-year cycle
year = year % 400;
// There are no leap years in years 100, 200 and 300 of the cycle.
int century = year / 100;
int leaps = year / 4 - century;
// A normal year is 52 weeks and 1 day, so the calendar advances one day.
// In a leap year, it advances two days.
int advances = year + leaps;
// This is either magic or carefully contrived,
// depending on how you look at it:
int month_offset = (13 * (month + 1)) / 5;
// From which, we can compute the day of week of the first of the month:
int first = (month_offset + advances) % 7;
// If the dayOfWeek we're looking for is at least the day we just
// computed, we just add the difference. Otherwise, we need to add 7.
// Then we just add the desired number of weeks.
int offset = dayOfWeek - first;
if (offset < 0) offset += 7;
return 1 + offset + (n - 1) * 7;
}
I made this operator for class where I store date, it is supposed to add given number of days to date and convert it back to human format
CDate CDate::operator+(const int x)
{
time_t t;
struct tm tmp = {0};
struct tm newtime = {0};
tmp.tm_year = timeinfo.tm_year;
tmp.tm_mon = timeinfo.tm_mon;
tmp.tm_mday = timeinfo.tm_mday + x;
t = mktime(&tmp);
newtime = *(localtime(&t));
return CDate(newtime.tm_year,newtime.tm_mon,newtime.tm_mday);
}
problem is it compute date 3 days higher that it should
concrete problem is, I have date 2000-01-02 (year-month-day) and i try to add 1500 days, expected result is 2004-02-10 but I am getting 2004-02-13
check
what am I doing wrong here?
my code:
http://pastebin.com/wLxzTe3W
You have two errors.
First, the tm passed to mktime is invalid: The year shouldn´t be the absolute year like 2000, but the year difference from 1900, ie. 100 for 2000. With this problem corrected, I get 2004-02-12 as result (neither 2004-02-13 nor 2004-02-10).
(On my computer, the program (without any changes) just crashes: mktime returns -1, localtime can´t handle it and returns an invalid pointer, which is used in the same line...)
Second, in the same struct, the day may be 1-31, but the month only 0-11 (not 1-12). Your 2000-01-02 is the second day of February, not January.
Knowing this, the result is correct (I´ll write the full year, but month starting from 0):
1500 days to add to 2000-01-02:
2000 has 366 days (leap year), and we don´t need January and the first February day:
2000-01-02 + (366-31-1) days = 2001-00-01, 1166 days to add left
Another 365 days makes 2002-00-01, 801 days to add left
Another 365 days makes 2003-00-01, 436 days to add left
Another 365 days makes 2004-00-01, 71 days to add left
Another 31 of January makes 2004-01-01, 40 days to add left
Another 29 of leap February makes 2004-02-01, 11 days to add left
Another 11 added makes 2004-02-12
Read http://www.cplusplus.com/reference/ctime/tm/ carefully
This is the algorithm for adding days to the date.
Calculate day number from date.
function g(y,m,d)
m = (m + 9) % 12
y = y - m/10
return 365*y + y/4 - y/100 + y/400 + (m*306 + 5)/10 + ( d - 1 )
To add no. of days in the date->
g(y2,m2,d2) + no_of_days
After this, you can convert your days back to date.
function d(g)
y = (10000*g + 14780)/3652425
ddd = g - (365*y + y/4 - y/100 + y/400)
if (ddd < 0) then
y = y - 1
ddd = g - (365*y + y/4 - y/100 + y/400)
endif
mi = (100*ddd + 52)/3060
mm = (mi + 2)%12 + 1
y = y + (mi + 2)/12
dd = ddd - (mi*306 + 5)/10 + 1
return y, mm, dd
I'm writing a program that has to convert the current gregorian date and time to a Julian Date and then back to Gregorian gate. Eventually I will need to add the functionality of being able to add years, months, days, hours, minutes and seconds, but I need to get this part out of the way first.
Right now I have the conversion from Gregorian Date to Julian Date, so logically I feel like I should simply be able to reverse the equation somehow and that that would be fairly simple. However I'm doing a two step process where I first convert the Gregorian Date to a Julian Day Number, and then to a Julian Date (difference being day number doesn't include time). So converting it back should just mean that I have to get the hours, minutes and seconds back from the equation, and then do the seperate conversion for Julian Day Number back to Gregorian date. I would like to think it's simple process of dividing and moding 3 times for hours, minutes and seconds, and normally I'm pretty good with math and thinking these things through logically, but my brain is simply not functioning on this one.
jdn_t gregorian_to_jd(year_t year, month_t month, day_t day, hour_t hour, minute_t minute, second_t second)
{
//implement the conversion from gregorian to jdn
long long a = (14 - month)/12;
long long y = year + 4800 - a;
long long m = month + 12*a - 3;
jdn_t jdn = day + (153 * m + 2)/5 + 365*y + y/4 - y/100 + y/400 - 32045 - 0.5;
jdnt_t jdnt = jdn + (hour-12)/24 + minute/1440 + second/86400;
}
void jdn_to_gregorianTime(jdnt_t jdnt,year_t & year, month_t & month, day_t & day, hour_t & hour, minute_t & minute, second_t & second)
{
long long j = static_cast<long long>(jdnt + 0.5) + 32044;
long long g = j / 146097;
long long dg = j % 146097;
long long c = (dg / 36524 + 1) * 3 / 4;
long long dc = dg - c * 36524;
long long b = dc / 1461;
long long db = dc % 1461;
long long a = (db / 365 + 1) *3 / 4;
long long da = db - a * 365;
long long y = g * 400 + c * 100 + b * 4 + a;
long long m = (da * 5 + 308) / 153 - 2;
long long d = da - (m+4) * 153 / 5 + 122;
year = y - 4800 + (m + 2) / 12;
month = (m + 2) % 12 + 1;
day = static_cast<day_t>(d + 1);
The bottom half there are the calculations I'll need once I've been able to get out my hours, minutes and seconds. All they do Is put the Julian Day Number back to Gregorian Date.
The wiki page explains the whole julian date thing for those who aren't farmiliar: http://en.wikipedia.org/wiki/Julian_day
I hope I've explained what I need well enough! Thanks for any help you guys can offer!
This free, open source C++11/14 date/time library uses the <chrono> foundation to facilitate conversions between any two calendars by setting up conversions from all calendars to and from Unix Time.
It happens to have a Julian calendar as well as two variants of the Gregorian calendar ({year, month, day} and {year, month, weekday, index}), the ISO week-based calendar, and an (imperfect) Islamic calendar. Calendars are relatively easily added, and once added a calendar is interoperable with all other calendars, and <chrono>'s system_clock::time_point at any precision.
Example code:
#include "date.h"
#include "julian.h"
#include <iostream>
int
main()
{
using namespace date::literals;
auto ymd = 2016_y/oct/11;
auto jymd = julian::year_month_day{ymd};
auto ymd2 = date::year_month_day{jymd};
std::cout << ymd << '\n';
std::cout << jymd << '\n';
auto ymd2 = date::year_month_weekday{jymd};
}
which outputs:
2016-10-11
2016-09-28
2016/Oct/Tue[2]
If you would like more details about the underlying algorithms, they are discussed (and proven) here:
http://howardhinnant.github.io/date_algorithms.html
You could just use this library.
http://www.iausofa.org/current_C.html
Or gain some insight by just looking it over and using the concepts.
I've used it before and it's pretty straight forward. Lots of pointers though so be prepared.
The one I do know about is cal2jd and the other is jd2cal.
Those get you the dates. There are more for time and formatting. It has some examples in the docs.
And if you are so inclines to want to C++ then there is
http://www.naughter.com/aa.html
which has functions for astronomical calculations.
Good luck!
Some other resources...
http://129.79.46.40/~foxd/cdrom/musings/formulas/formulas.htm
http://robm.fastmail.fm/articles/date_class.html
https://www.autoitscript.com/forum/topic/182372-standalone-moon-phase-calculation/
What are the default values taken (say 1721119) to calculate the Gregorian Year, Month, Day from Julian Day
http://www.projectpluto.com/source.htm
This is one solution where the full time is returned as hhmmss format but you get them sperately. See the end of the function at
//hours: secs/3600 % 24, min: secs/60 % 60, secs secs % 60
unsigned JulianToTime(double julianDate)
{
double remainder = julianDate - (unsigned)julianDate;
const unsigned long long base = 1000000;
const unsigned long long halfbase = 500000;
const unsigned secsPerDay = 86400;
// "rounded" remainder after adding half a day
unsigned long long rndRemainder = (unsigned long long)(remainder * base + halfbase) % base;
rndRemainder *= secsPerDay;
// "rounded" number of seconds
unsigned long long nsecs = (rndRemainder + halfbase) / base;
//hours: secs/3600 % 24, min: secs/60 % 60, secs secs % 60
unsigned rtn = (nsecs/3600 % 24) * 10000 + (nsecs/60 % 60) * 100 + (nsecs % 60);
return rtn;
}