c++ adding years and days using date.h - c++

Working on calendar duration arithmetic using date.h and std::chrono, but getting an unexpected result.
Sample code is:
#include "date.h"
#include <string>
#include <chrono>
#include <iostream>
int main() {
date::sys_seconds calendarDate = {};
calendarDate = std::chrono::years(30) + date::sys_seconds(std::chrono::days(10));
std::string stringDate = date::format("%Y-%m-%d %H:%M:%S", calendarDate);
std::cout << "{} + 30 years + 10 days = " << stringDate << "\n";
return 0;
}
Actual Output:
{} + 30 years + 10 days = 2000-01-11 06:36:00
Expected Output:
{} + 30 years + 10 days = 2000-01-11 00:00:00
Using Ubuntu 22.04; g++ 11.3.0
Compiled with: gcc -g -std=c++20 main.cpp -lstdc++
Using date.h fromm here: https://raw.githubusercontent.com/HowardHinnant/date/master/include/date/date.h
Any insight into what's adding in the extra 6hours and 36minutes?

It is because of leap time. For the Gregorian calendar, the average length of the calendar year (the mean year) across the complete leap cycle of 400 years is 365.2425 days (97 out of 400 years are leap years).
This is taken into account in the definition of std::chrono::years which is defined as a duration type with Period std::ratio<31556952> (60 * 60 * 24 * 365.2425 = 31556952).
However, if you compare this to the non-leap-aware duration of a year (60 * 60 * 24 * 365 = 31536000), then you get a difference of 20952 seconds or 5 hours 49 minutes and 12 seconds. If you multiply that difference by 30 it grows to:
7 days 6 hours 36 minutes and 0 seconds - and at least the latter part should look familiar to you.
But what about the 7 days? Well, it turns out that between 1970 and 2000: 1972, 1976, 1980, 1984, 1988, 1992, and 1996 were leap years - if you count that is 7, and that is where the days went.
You can check this by yourself by changing your code and looking at the output:
calendarDate += std::chrono::seconds(60 * 60 * 24 * 365 * 30); - "1999-12-25 00:00:00"
calendarDate += std::chrono::seconds(60 * 60 * 24 * 365 * 30) + day(7); - "2000-01-01 00:00:00"
By the way, the problem is that std::chrono::years gives you the length of an average year, while the date.h library knows (and cares about) the specific years: It knows that 1970 was not a leap year (so 60 * 60 * 24 * 365 = 31536000 seconds), but 1972 was a leap year (so 60 * 60 * 24 * 366 = 31622400 seconds).
If you had added 31 years instead, then you would have added 5:49:12 more skew from the average year, but then removed 1 more day since 2000 was also a leap year - so the time it would print would still be in 2000, but ~12 hours before new-years 2001.

In addition to Frodyne's good answer (which I've upvoted), I wanted to add that you can do either chronological arithmetic or calendrical arithmetic with date.h.
You've done chronological arithmetic, where years is considered to be a uniform unit, and is equal to the average civil year.
Calendrical arithmetic follows the irregularity of calendars, and is what you expected. This is how you would accomplish that:
#include "date/date.h"
#include <string>
#include <chrono>
#include <iostream>
int main() {
auto chronologicalDateTime = date::sys_seconds(date::days(10));
auto chronologicalDate = date::floor<date::days>( chronologicalDateTime);
date::year_month_day calendarDate = chronologicalDate;
auto timeOfDay = chronologicalDateTime - chronologicalDate;
calendarDate += date::years{30};
chronologicalDateTime = date::sys_days{calendarDate} + timeOfDay;
std::string stringDate = date::format("%Y-%m-%d %H:%M:%S", chronologicalDateTime);
std::cout << "{} + 30 years + 10 days = " << stringDate << "\n";
return 0;
}
You first convert the chronological date/time to a calendrical date, and add the years to the calendrical date. Then convert that back into a chronological date/time.
{} + 30 years + 10 days = 2000-01-11 00:00:00
Here's another SO answer that goes over the same principle, except using months: https://stackoverflow.com/a/43018120/576911

Linux stores dates as UTC. My guess is that your machine is located at Central Time zone (CT) which is +6 hours from UTC.

Related

how to sorting date in array C++

I have a date in the form of ddmmyyyy in array
int checkIn[n], checkOut[n];
int a = sizeof(checkIn) / sizeof(checkIn[0]);
int a2 = sizeof(checkOut) / sizeof(checkOut[0]);
sort(checkIn, checkIn + a);
sort(checkOut, checkOut + a2);
Input:
3
08022022 15022022
10022025 14022025
15032022 20032022
Output:
8022022 10022025 15032022
How to fix. Thank you
I think the best idea to solve this date sort in C++ is to use this struct:
struct tm {
int tm_sec; // seconds of minutes from 0 to 61
int tm_min; // minutes of hour from 0 to 59
int tm_hour; // hours of day from 0 to 24
int tm_mday; // day of month from 1 to 31
int tm_mon; // month of year from 0 to 11
int tm_year; // year since 1900
int tm_wday; // days since sunday
int tm_yday; // days since January 1st
int tm_isdst; // hours of daylight savings time
}
You should load the dates you want to sort in this struct and then you can use mktime() to get the time_t for using the following function:
double difftime (time_t end, time_t beginning);
(https://www.cplusplus.com/reference/ctime/difftime/)
This function will calculate the difference between the time_t's. Then you can sort the list or the array. For example for the list you can do: https://www.cplusplus.com/reference/list/list/sort/

How would you get the epoch time and convert it to local time in arm assembly on a raspberry pi?

So I am programming in arm assembly on raspbian and I am trying to convert the epoch time using c/c++ libraries because that is what I am allowed to do, but I am confused as to how to do it. If I simply bl time it will give me the epoch time, but I am confused as to how I would get the return value in r0, then convert that into the local time in assembly using C or C++ libraries. I know localtime/gmtime and strftime exist, but its not as easy as getting the epoch and just bl localtime or bl strftime. Then I want to format it where I only get the local time and maybe am/pm. I am not interested in the date. I just need some helpful code, or some direction to be pushed into. Thanks
Edit: If its easier to just convert using math that would also be helpful
So to not do your homework for you, here is an example. All of this is basic grade school math, no magic.
If I have 345678 pennies how does that brake down into various dollar and coin amounts.
There are 100 pennies in a dollar and 100 dollars in a 100 dollar bill, so
345678 / (100*100) = 34 remainder 5678
Looking at units
pennies / (pennies/dollar * dollars/hundred) =
(pennies * dollars * hundreds) / (pennies * dollars )
pennies and dollars cancel out left with hundreds which is correct
so 34 hundred dollar bills with a remainder of 5678 pennies
repeat for 20 dollar bills
5678 / (100 * 20) = 2 remainder 1678
10 dollar bills
1678 / (100 * 10) = 1 remainder 678
5 dollar bills
678 / (100 * 5) = 1 remainder 178
one dollar bills
178 / (100 * 1) = 1 remainder 78
50 cent pieces
78 / 50 = 1 remainder 28
quarters (25 cents)
28 / 25 = 1 remainder 3
dimes (10 cents) 0 remainder 3
nickles (5 cents) 0 remainder 3
pennies the remainder 3
so 345678 pennies is equal to
34 100 dollar bills +
2 20 dollar bills +
1 10 dollar bill +
1 5 dollar bill +
1 1 dollar bill +
1 half dollar coin +
1 quarter +
3 pennies
check the work
34 * 100 * 100 = 340000
2 * 100 * 20 = 4000
1 * 100 * 10 = 1000
1 * 100 * 5 = 500
1 * 100 = 100
1 * 50 = 50
1 * 25 = 25
1 * 3 = 3
add that up you get 345678
If I simply wanted to know how many quarters
345678 / (25 * 1) = 13827 quarters with a remainder of 3.
it all works the same with 60 seconds per minute 60 minutes per hour 24 hours per day. 365 days for 1970, 365 days for 1971, 366 days for 1972 365 days for 1973 and so on
31 days for january, 28 days for february 2019, 31 days for march and so on
easier to adjust for timezone first 60 seconds * 60 minutes * hours of adjustment
add or subtract that off as needed, then work that number either through the years/months/days or if you simply care about time of day then you only need to divide by seconds per day. or you can divide by seconds per day and get total days as a result with fraction of a day as a remainder, the fraction of a day is the time of day today and the total days you can then later subtract off the years then months to find the date.
Extra credit, what year will computers with 32 bit time counters using the 1970 epoch have a Y2K like roll over event (causing crashes and death and destruction across the planet just like Y2K)?
The programming language is irrelevant until the algorithm is understood and ideally coded in a favorite high level language, to confirm/prove the algorithm. Then port that knowledge to some other programming language.
Shortcutting the steps will sometimes get you there faster but when the shortcut fails it fails in spectacular fashion.

Converting a timestamp to freshness index

I have a column in dataframe which has article and its publication date (timestamp). I need to use this information to find out a freshness score of an article.
articleId publicationDate
0 581354 2017-09-17 15:16:55
1 581655 2017-09-18 07:37:51
2 580864 2017-09-16 06:44:39
3 581610 2017-09-18 06:30:30
4 581605 2017-09-18 07:22:24
Most recent article should get higher score. Timewindow should be half an hour (2 articles published in half an hour must get same score)
Some of the code below might be redundant but it seems to work:
df['score'] = df['publicationDate'] - df['publicationDate'].max()
df['score'] = (df['score'] / np.timedelta64(1, 'm')).apply(lambda x: (round(x / 30) * 30 + 30) / 30 if x else x).rank(method='max')
So you convert timedelta to minutes, then round it to 30, and finally rank that value.
It can also be a one-liner if you please:
df['score'] = ((df['publicationDate'] - df['publicationDate'].max()) / np.timedelta64(1, 'm')).apply(lambda x: (round(x / 30) * 30 + 30) / 30 if x else x).rank(method='max')
Explaination:
(df['publicationDate'] - df['publicationDate'].max() - subtract all dates from most recent one
(df['score'] / np.timedelta64(1, 'm')) - convert timedelta into minutes
.apply(lambda x: (round(x / 30) * 30 + 30) / 30 if x else x) - roundup to 30 minutes excluding most recent timestamp
.rank(method='max') rank the results giving upper value to all those that have same rank.
EDIT:
To change rank of those older than 2 days you can use this:
df['diff'] = (df['publicationDate'] - df['publicationDate'].max()).apply(lambda x: x.days)
df.loc[df['diff']<=-2, 'score'] = 0
First line will give you timedelta in whole days, and second one will change rank to 0 where days are less or equal to -2.

How to find the day of the week `tm_wday` from a given date?

To find the day (number) for a given date, I wrote below code using <ctime>:
tm time {ANY_SECOND, ANY_MINUTE, ANY_HOUR, 21, 7, 2015 - 1900};
mktime(&time); // today's date
PRINT(time.tm_wday); // prints 5 instead of 2 for Tuesday
According to the documentation, tm_wday can hold value among [0-6], where 0 is Sunday. Hence for Tuesday (today), it should print 2; but it prints 5.
Actually tm_wday gives consistent results, but with a difference of 3 days.
What is wrong here?
You got the month wrong, tm_mon is the offset since January, so July is 6. From the manpage:
tm_mon The number of months since January, in the range 0 to 11.
This outputs 2:
#include <stdio.h>
#include <string.h>
#include <time.h>
int main(void) {
struct tm time;
memset(&time, 0, sizeof(time));
time.tm_mday = 21;
time.tm_mon = 6;
time.tm_year = 2015-1900;
mktime(&time);
printf("%d\n", time.tm_wday);
return 0;
}
Note that you should initialize the other fields to 0 with memset(3) or similar.
The reason you are getting invalid output is that you are using the wrong month. tm_mon starts at 0 and not 1. you can see tghis by using this code:
tm time {50, 50, 12, 21, 7, 2015 - 1900};
time_t epoch = mktime(&time);
printf("%s", asctime(gmtime(&epoch)));
Output:
Fri Aug 21 12:50:50 2015
Live Example

Get the number of trading days in between two days

i'm trying to get the number of trading dates between two dates which will only exclude the weekends and won't consider any holidays. I'm using Boost and c++11 standards.
using namespace boost::gregorian;
long dateDifference( string start_date, string end_date ) {
date _start_date(from_simple_string(start_date));
date _end_date(from_simple_string(end_date));
long difference = ( _start_date - _end_date ).days();
return difference;
}
This just returns the number of days between two dates without considering weekends. Can someone point me in the right direction. I can't seem to figure out the solution.
Thanks,
Maxx
O(1) solution with no loops:
#include <boost/date_time.hpp>
using namespace std;
using namespace boost::gregorian;
long countWeekDays( string d0str, string d1str ) {
date d0(from_simple_string(d0str));
date d1(from_simple_string(d1str));
long ndays = (d1-d0).days() + 1; // +1 for inclusive
long nwkends = 2*( (ndays+d0.day_of_week())/7 ); // 2*Saturdays
if( d0.day_of_week() == boost::date_time::Sunday ) ++nwkends;
if( d1.day_of_week() == boost::date_time::Saturday ) --nwkends;
return ndays - nwkends;
}
The basic idea is to first count all Saturdays, which is conveniently given by the formula (ndays+d0.day_of_week())/7. Doubling this gives you all Saturdays and Sundays, except in the cases when the start and end dates may fall on a weekend, which is adjusted for by 2 simple tests.
To test it:
#include <iostream>
#include <cassert>
#include <string>
// January 2014
// Su Mo Tu We Th Fr Sa
// 1 2 3 4
// 5 6 7 8 9 10 11
// 12 13 14 15 16 17 18
// 19 20 21 22 23 24 25
// 26 27 28 29 30 31
int main()
{
assert(countWeekDays("2014-01-01","2014-01-01") == 1);
assert(countWeekDays("2014-01-01","2014-01-02") == 2);
assert(countWeekDays("2014-01-01","2014-01-03") == 3);
assert(countWeekDays("2014-01-01","2014-01-04") == 3);
assert(countWeekDays("2014-01-01","2014-01-05") == 3);
assert(countWeekDays("2014-01-01","2014-01-06") == 4);
assert(countWeekDays("2014-01-01","2014-01-10") == 8);
assert(countWeekDays("2014-01-01","2014-01-11") == 8);
assert(countWeekDays("2014-01-01","2014-01-12") == 8);
assert(countWeekDays("2014-01-01","2014-01-13") == 9);
assert(countWeekDays("2014-01-02","2014-01-13") == 8);
assert(countWeekDays("2014-01-03","2014-01-13") == 7);
assert(countWeekDays("2014-01-04","2014-01-13") == 6);
assert(countWeekDays("2014-01-05","2014-01-13") == 6);
assert(countWeekDays("2014-01-06","2014-01-13") == 6);
assert(countWeekDays("2014-01-07","2014-01-13") == 5);
cout << "All tests pass." << endl;
return 0;
}
This works for any date range in the Gregorian calendar, which boost currently supports for years 1400-10000. Note that different countries have adopted the Gregorian calendar at different times. For example the British switched from the Julian to the Gregorian calendar in September of 1752, so their calendar for that month looks like
September 1752
Su Mo Tu We Th Fr Sa
1 2 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Just run a day iterator and calculate the weekdays manually:
#include <boost/date_time.hpp>
using namespace boost::gregorian;
long dateDifference( string start_date, string end_date )
{
date _start_date(from_simple_string(start_date));
date _end_date(from_simple_string(end_date));
// counter for weekdays
int cnt=0;
for(day_iterator iter = _start_date; iter!=_end_date; ++iter)
{
// increment counter if it's no saturday and no sunday
if( iter->day_of_week() != boost::date_time::Saturday
&& iter->day_of_week() != boost::date_time::Sunday)
++cnt;
}
return cnt;
}
Answer ported from this answer: https://stackoverflow.com/a/7342989/3187827
The simplest approach is to use the boost::gregorian::day_of_week() function and iterate through every day between your start date and your end date, incrementing only when it's not a Saturday nor a Sunday.
A more efficient approach would be to iterate from start_date to the next Monday (say), iterate from end_date to the previous Monday, then with a simple division find out how many week-ends you've got in between.
Finally, a "real world" solution would involve finding proper calendar data with the holidays that apply to your case, and integrate it with your algorithm.
I didn't find any O(1) solutions which satisfied me so here is what I did:
int get_weekdays_count(const boost::gregorian::date& a,const boost::gregorian::date& b)
{
int na=(a<b) ? a.day_of_week().as_number() : b.day_of_week().as_number();
int diff=(a-b).days();
if(diff!=0){
if(diff<0) diff*=-1;
int rslt=diff/7; //number of saturdays
rslt*=2; // times 2 for sundays
rslt+= (diff%7) >=(boost::gregorian::Saturday-na)%7 ? 1 : 0; // handle special case for saturdays
rslt+= (diff%7) >=(boost::gregorian::Sunday-na)%7 ? 1 : 0; //special case for sundays
return 1+diff-rslt;
}
else return (na==boost::gregorian::Saturday || na==boost::gregorian::Sunday) ? 0 : 1;
};
This work even if a > b , just put two separate time and here you go.
Speed in a for loop: 25 nanosec/call on VS2012 Release mode // proc i5 4690K