Calculating the date from day-number? - c++

How would one go about calculating a date from a day-number in C++? I don't require you to write the whole code, I just can't figure out the maths to calculate the month and the day-of-month!
Example:
input: 1
output: 01/01/2012
input: 10
output: 01/10/2012
input: 365
output: 12/31/2012
It would always use the current year, if they exceeded 365, I would return 0. There is no need for a leap-year detection.

Use a date calc library as e.g. the fine Boost Date_Time library with which this becomes
using namespace boost::gregorian;
date d(2012,Jan,1); // or one of the other constructors
date d2 = d + days(365); // or your other offsets

It's not even very hard with the standard library. Forgive me if I write C++ code like a C programmer (the C++ <ctime> has no reentrant gmtime function):
#include <time.h>
#include <cstdio>
int main(int argc, char *argv[])
{
tm t;
int daynum = 10;
time_t now = time(NULL);
gmtime_r(&now, &t);
t.tm_sec = 0;
t.tm_min = 0;
t.tm_hour = 0;
t.tm_mday = 1;
t.tm_mon = 1;
time_t ref = mktime(&t);
time_t day = ref + (daynum - 1) * 86400;
gmtime_r(&day, &t);
std::printf("%02d/%02d/%04d\n", t.tm_mon, t.tm_mday, 1900 + t.tm_year);
return 0;
}
Sorry, I don't know a sane way to do this without leap-year detection.

A simple snippet from a program, assuming 365 days in a year:
int input, day, month = 0, months[13] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
while (input > 365) {
// Parse the input to be less than or equal to 365
input -= 365;
}
while (months[month] < input) {
// Figure out the correct month.
month++;
}
// Get the day thanks to the months array
day = input - months[month - 1];

Related

Convert unix timestamp to human readable date

Is there a contemporary way to convert unix timestamps to a human readable date?
Since I want to circumnavigate the year 2038 problem, I want to use int64s.
I target to convert e. g. 1205812558 to
year = 2008, month = 3, day = 18,
hour = 17, minute = 18, second = 36
All I have is now
auto year = totalSeconds / secondsPerYear + 1970;
// month and day missing
auto hours = totalSeconds / 3600 % 24;
auto minutes = totalSeconds / 60 % 60;
auto seconds = totalSeconds % 60;
In C++20 (according to the draft-spec for C++20 as it stands today), you will be able to say:
#include <chrono>
#include <iostream>
int
main()
{
using namespace std;
using namespace std::chrono;
cout << sys_seconds{1205812558s} << '\n';
cout << sys_seconds{32879409516s} << '\n';
}
and it will output:
2008-03-18 03:55:58
3011-11-28 17:18:36
These are datetimes in UTC.
You can use Howard Hinnant's date library to experiment with this extended <chrono> functionality today by adding:
#include "date/date.h"
and
using namespace date;
to the above program. You can experiment online with this program here.
A comment below asks for what this looks like if the value is stored in uint64_t. The answer is that you need to convert the integral type to seconds, and then the seconds to sys_seconds:
uint64_t i = 1205812558;
cout << sys_seconds{seconds(i)} << '\n';
There do exist limits on this contemporary functionality, but they live out near the years +/-32K (far beyond the limits of the accuracy of the current civil calendar).
To be completely transparent, there do exist ways of doing this using only C++98/11/14/17, but they are more complicated than this, and are subject to multithreading bugs. This is due to the use of an antiquated C API that was designed before things like multithreading and C++ were on the horizon, and when the year 2001 was only associated with science fiction (e.g. gmtime).
Wrapper
#include <chrono>
char* get_time(time_t unix_timestamp)
{
char time_buf[80];
struct tm ts;
ts = *localtime(&unix_timestamp);
strftime(time_buf, sizeof(time_buf), "%a %Y-%m-%d %H:%M:%S %Z", &ts);
return time_buf;
}
Howard Hinnant's date library makes things pretty easy:
#include "date.h"
int main()
{
using namespace date;
time_t time = 32879409516;
auto sysTime = std::chrono::system_clock::from_time_t(time);
auto date = year_month_day(floor<days>(sysTime));
std::cout << date << "\n";
}
A good straight forward solution but could do with some minor changes:
uint32_t days = (uint32_t)floor(subt / 86400);
uint32_t hours = (uint32_t)floor(((subt - days * 86400) / 3600) % 24);
uint32_t minutes = (uint32_t)floor((((subt - days * 86400) - hours * 3600) / 60) % 60);
uint32_t seconds = (uint32_t)floor(((((subt - days * 86400) - hours * 3600) - minutes * 60)) % 60);
printf("Time remaining: %u Days, %u Hours, %u Minutes, %u Seconds\n", days, hours, minutes, seconds);

Arduino printing of two-digit month format

I have this code in the setup for my Arduino to create a filename using the date. It is working however there is a problem.
#include <DS3231.h>
#include <SD.h>
#include <SPI.h>
#include <dht.h>
dht DHT;
Time now;
int dt;
int t;
unsigned int interation = 1;
char filename[12];
DS3231 rtc(SDA, SCL);
void setup() {
Serial.begin(9600);
rtc.begin(); // Initialize the rtc object
rtc.setDOW(THURSDAY); // Set Day-of-Week to SUNDAY
rtc.setTime(21, 48, 0); // Set the time to 12:00:00 (24hr format)
rtc.setDate(10, 11, 2017); // Set the date to January 1st, 2014
now = rtc.getTime();
String(String(now.year) + String(now.mon) + String(now.dow) + ".csv").toCharArray(filename, 12);
Serial.println(filename);
It is printing a string of the date but there is no leading zero in the month digit when it is a single digit.
The code prints this 2017111.csv instead of 20170111.csv. How can I fix this?
You need an if statement to test if the number is less than 10 and if so add your own 0.
String myMonthString = "";
int mon = now.mon;
if(mon < 10){
myMonthString += '0';
}
myMonthString += mon;
A far more elegant solution would be to use sprintf. This also doesn't use the String class which can do some bad things on little microcontrollers and is generally to be avoided on Arduino.
char fileName[12];
sprintf(fileName, "%d%02d%02d.csv", now.year, now.mon, now.dow);

How to get the first Sunday of year in c++?

I tried to get the date of first Sunday in this year
int getFristSunday () {
time_t rawtime;
struct tm * timeinfo;
time( &rawtime );
timeinfo = localtime( &rawtime );
timeinfo->tm_mon = 0;
timeinfo->tm_wday = 0;
mktime( timeinfo );
return timeinfo->tm_yday ;
}
but I get the first Thursday
Result
From this mktime reference:
time->tm_wday and time->tm_yday are ignored.
You have to set timeinfo->tm_mday to 1 and then check what day it is after calling mktime, and count forward from there.
Using this free, open-source, header-only C++11/14 library:
#include "date.h"
#include <iostream>
int
main()
{
using namespace date;
std::cout << year_month_day{sun[1]/jan/2016} << '\n';
}
which outputs:
2016-01-03
There are year(), month() and day() accessors for the year_month_day object. And the algorithms are highly optimized (containing no iterative loops).
If you would prefer to write your own date computations, here are the public domain calendrical algorithms used in the aforementioned date library. The link goes straight to the section describing how to find the Nth day of the week of the month/year combination.
Before calling mktime() all fields, except tm_yday and tm_wday need to be set. Obviously we need to set tm_mon and tm_mday for Jan 1.
Important to set the tm_hour to midday (12) and/or tm_isdst to -1 to insure the recalculated time is not impacted by daylight saving time. Consider what would happen if the current time was near midnight and the DST setting for now was different than Jan 1. The re-calculation could push time out of Jan 1 to Jan 2 or Dec 31.
int getFirstSunday(void) {
time_t rawtime;
struct tm * timeinfo;
time(&rawtime);
timeinfo = localtime(&rawtime);
timeinfo->tm_mon = 0; // set to January which is 0 "Months since January"
timeinfo->tm_mday = 1; // Set to the 1st of the month
timeinfo->tm_hour = 12; // Set to avoid getting the wrong DST setting for Jan 1.
timeinfo->tm_isdst = -1; // Set to avoid getting the wrong DST setting for Jan 1.
if (mktime(timeinfo) == -1) return -1;
int DaysSinceSundayForJan1 = timeinfo->tm_wday; // days since Sunday — [0, 6]
int DaysAfterJan1toNextSunday = 7 - DaysSinceSundayForJan1;
int DaysAfterJan1toFirstSunday = DaysAfterJan1toNextSunday%7;
// Convert to "day of the month"
return DaysAfterJan1toFirstSunday + 1;
}

How to compare range of date in C/C++?

Is there any system defined function to compare two dates in C/C++?
Thanks
Here is my favorite date class (C++ only, not C):
http://howardhinnant.github.io/date.html
With this you can write programs like:
#include "date.h"
#include <cassert>
int main()
{
using namespace gregorian;
date d1 = thu[last]/mar/2011; // last Thursday in March 2011
date d2 = mar/31/2011; // March 31, 2011
assert(d1 == d2); // The last Thursday in March 2011 is 3/31/2011
d1 += month(1); // last Thursday in April 2011
assert(d1 > d2); // d1 is later than d2
assert(d1 == month(4)/28/2011); // d1 is now Apr. 28, 2011
}
The software is free to use. You don't even have to credit anyone. 1 header, 1 source.
Update
Latest version of this software is here: https://howardhinnant.github.io/date/date.html
convert the date (you pick the format) to seconds since the start of the era. use strptime and mktime.
compare the two time_t (seconds) values.
example using MON-DD-YYYY format:
CODE:
#include <time.h>
time_t to_seconds(const char *date)
{
struct tm storage={0,0,0,0,0,0,0,0,0};
char *p=NULL;
time_t retval=0;
p=(char *)strptime(date,"%d-%b-%Y",&storage);
if(p==NULL)
{
retval=0;
}
else
{
retval=mktime(&storage);
}
return retval;
}
int main()
{
char *date1="20-JUN-2006";
char *date2="21-JUN-2006";
time_t d1=to_seconds(date1);
time_t d2=to_seconds(date2);
printf("date comparison: %s %s ",date1,date2);
if(d1==d2) printf("equal\n");
if(d2>d1) printf("second date is later\n");
if(d2<d1) printf("seocnd date is earlier\n");
return 0;
}
Inspired by Bo Persson's comment:
bool compare_DD_MM_YYYY_dates(std::string date1, std::string date2)
{
assert(date1.size()>=10); assert(date2.size()>=10);
// ISO-fy dates
date1 = date1.substr(6,4) + date1.substr(3,2) + date1.substr(0,2) + date1.substr(10);
date2 = date2.substr(6,4) + date2.substr(3,2) + date2.substr(0,2) + date2.substr(10);
return date1 < date2;
}
If not fast enough, don't concatenate the substrings, but compare them directly.

Converting epoch time to "real" date/time

What I want to do is convert an epoch time (seconds since midnight 1/1/1970) to "real" time (m/d/y h:m:s)
So far, I have the following algorithm, which to me feels a bit ugly:
void DateTime::splitTicks(time_t time) {
seconds = time % 60;
time /= 60;
minutes = time % 60;
time /= 60;
hours = time % 24;
time /= 24;
year = DateTime::reduceDaysToYear(time);
month = DateTime::reduceDaysToMonths(time,year);
day = int(time);
}
int DateTime::reduceDaysToYear(time_t &days) {
int year;
for (year=1970;days>daysInYear(year);year++) {
days -= daysInYear(year);
}
return year;
}
int DateTime::reduceDaysToMonths(time_t &days,int year) {
int month;
for (month=0;days>daysInMonth(month,year);month++)
days -= daysInMonth(month,year);
return month;
}
you can assume that the members seconds, minutes, hours, month, day, and year all exist.
Using the for loops to modify the original time feels a little off, and I was wondering if there is a "better" solution to this.
Be careful about leap years in your daysInMonth function.
If you want very high performance, you can precompute the pair to get to month+year in one step, and then calculate the day/hour/min/sec.
A good solution is the one in the gmtime source code:
/*
* gmtime - convert the calendar time into broken down time
*/
/* $Header: gmtime.c,v 1.4 91/04/22 13:20:27 ceriel Exp $ */
#include <time.h>
#include <limits.h>
#include "loc_time.h"
struct tm *
gmtime(register const time_t *timer)
{
static struct tm br_time;
register struct tm *timep = &br_time;
time_t time = *timer;
register unsigned long dayclock, dayno;
int year = EPOCH_YR;
dayclock = (unsigned long)time % SECS_DAY;
dayno = (unsigned long)time / SECS_DAY;
timep->tm_sec = dayclock % 60;
timep->tm_min = (dayclock % 3600) / 60;
timep->tm_hour = dayclock / 3600;
timep->tm_wday = (dayno + 4) % 7; /* day 0 was a thursday */
while (dayno >= YEARSIZE(year)) {
dayno -= YEARSIZE(year);
year++;
}
timep->tm_year = year - YEAR0;
timep->tm_yday = dayno;
timep->tm_mon = 0;
while (dayno >= _ytab[LEAPYEAR(year)][timep->tm_mon]) {
dayno -= _ytab[LEAPYEAR(year)][timep->tm_mon];
timep->tm_mon++;
}
timep->tm_mday = dayno + 1;
timep->tm_isdst = 0;
return timep;
}
The standard library provides functions for doing this. gmtime() or localtime() will convert a time_t (seconds since the epoch, i.e.- Jan 1 1970 00:00:00) into a struct tm. strftime() can then be used to convert a struct tm into a string (char*) based on the format you specify.
see: http://www.cplusplus.com/reference/clibrary/ctime/
Date/time calculations can get tricky. You are much better off using an existing solution rather than trying to roll your own, unless you have a really good reason.
An easy way (though different than the format you wanted):
std::time_t result = std::time(nullptr);
std::cout << std::asctime(std::localtime(&result));
Output:
Wed Sep 21 10:27:52 2011
Notice that the returned result will be automatically concatenated with "\n".. you can remove it using:
std::string::size_type i = res.find("\n");
if (i != std::string::npos)
res.erase(i, res.length());
Taken from: http://en.cppreference.com/w/cpp/chrono/c/time
time_t t = unixTime;
cout << ctime(&t) << endl;
This code might help you.
#include <iostream>
#include <ctime>
using namespace std;
int main() {
// current date/time based on current system
time_t now = time(0);
// convert now to string form
char* dt = ctime(&now);
cout << "The local date and time is: " << dt << endl;
// convert now to tm struct for UTC
tm *gmtm = gmtime(&now);
dt = asctime(gmtm);
cout << "The UTC date and time is:"<< dt << endl;
}
To convert a epoch string to UTC
string epoch_to_utc(string epoch) {
long temp = stol(epoch);
const time_t old = (time_t)temp;
struct tm *oldt = gmtime(&old);
return asctime(oldt);
}
and then it can be called as
string temp = "245446047";
cout << epoch_to_utc(temp);
outputs:
Tue Oct 11 19:27:27 1977
If your original time type is time_t, you have to use functions from time.h i.e. gmtime etc. to get portable code. The C/C++ standards do not specify internal format (or even exact type) for the time_t, so you cannot directly convert or manipulate time_t values.
All that is known is that time_t is "arithmetic type", but results of arithmetic operations are not specified - you cannot even add/subtract reliably. In practice, many systems use integer type for time_t with internal format of seconds since epoch, but this is not enforced by standards.
In short, use gmtime (and time.h functionality in general).