I'm making a program that needs the duration (in time_t) of a year.
In other ways, time_t of DD/MM/YYYY + duration = time_t of DD/MM/YYYY+1
So it may not always be 365 days (and 29/02/2012 will become 28/02/2013)
Here's the algorithm I came with :
if YEAR is leap than
if we are before the 29th feb' than return 365+1 days
else if we are the 29th feb' than return 365-1 days
else return 365 days
else if YEAR+1 is leap than
if we are before or the 28th feb' than return 365 days
else return 365+1 days
else return 365 days
Here, a day is 60 * 60 * 24 seconds
This algorithm seems to work. But I was wondering if there were another way to do this without all theses conditions and only 2 possible return values, or just some "trick" to optimize the thing.
I tried to increment tm_year from the struct tm like this :
// t is the input time_t
struct tm Tm (*localtime(&t));
if (Tm.tm_mon == 2 && Tm.tm_mday == 29) --Tm.tm_mday;
++Tm.tm_year;
return mktime(&Tm) - t;
But the result isn't what I want, I got -1 hour, or -25...
I guess it's because a year is not exactly 365 * 24 * 60 * 60.
I would use Boost for this, since it already implements what you are looking for:
#include <iostream>
#include <boost/date_time/gregorian/gregorian_types.hpp>
namespace date = boost::gregorian;
int main() {
date::date_period dp(date::date(2012, 6, 4), date::date(2013, 6, 4));
long days = dp.length().days();
std::cout << "Days between dates: " << days << std::endl;
}
If you wanted more precision, then you could use posix_time from Boost too:
namespace ptime = boost::posix_time;
...
ptime::ptime t1(date::date(2012, 6, 4), ptime::hours(0));
ptime::ptime t2(date::date(2013, 6, 4), ptime::hours(0));
ptime::time_duration td = t2 - t1;
std::cout << "Milliseconds: " << td.total_milliseconds() << std::endl;
Typically time_t is measured in seconds. Therefore, you would just need to call to td.total_seconds() to obtain the value that you are looking for.
if YEAR is leap than
if we are before the 29th feb' than return 365+1 days
else if we are the 29th feb' than return 365-1 days
else return 365 days
else if YEAR+1 is leap than
if we are before or the 28th feb' than return 365 days
else return 365+1 days
else return 365 days
simplifies to:
if (YEAR is leap)
if (< 29th Feb) return 365+1
if (= 29th Feb) return 365-1
else if (YEAR+1 is leap)
if (> 29th Feb) return 365+1
return 365
But why would you want to do this? It's much better to have readable code than "trick" optimizations.
As #betabandido has suggested, something like date(year+1, mon, day) - date(year, mon, day) would be much simpler, far more readable and able to handle leap years, leap seconds and September missing 11 days.
The length of a solar year is not a fixed number. The Gregorian calendar has invented a method to compensate for leap years which is not entirely precise. That goes "a year is a leap year if it is divisible by 4, unless it is divisible by 100, but is leap again if it is divisible by 400.
We in Iran have a more precise calendar, in which the years change the second Earth makes a full circle around the sun. In the same link you can see the average solar year as 365.2422 days and mean interval between spring equinoxes as 365.2424 days.
In this link more details are given about the length of the solar year (tropical year) in seconds.
Related
I get week number in ISO 8601 format (%V) using strftime and like to convert to week number shown in my outlook (guess it is gregorian).
Outlook calendar setting is:
First Day of the week (Sunday)
First week of the year starts 1st Jan
For certain years, strftime matches with weeknumber shown in outlook, but for some it doesn't.
A simple code check:
#include <stdio.h>
#include <time.h>
int main ()
{
time_t rawtime;
struct tm *tmDate;
char buffer [80];
time (&rawtime);
tmDate = localtime(&rawtime);
tmDate->tm_hour = 12;
tmDate->tm_mday = 1; //Day of the Month
tmDate->tm_mon = 8; //Month (0=Jan...8=Sep)
tmDate->tm_year = 2018-1900; //Year
mktime(tmDate);
strftime (buffer,80,"%Y-W%V",tmDate);
puts (buffer);
return 0;
}
For above code with it's input, the output will be 2018-W35, which matches with my outlook calendar(https://www.calendar-365.com/2018-calendar.html).
Whereas, if you change year to 2019, the output will be 2019-W35, but in my outlook it falls on 2019-W36 (https://www.calendar-365.com/2019-calendar.html).
Is it possible to map ISO8601 week number to gregorian style?
Any suggestions or code sample will be helpful!
Thank you!
The website you reference seems to use a very unique week numbering system. Its definition of week number appears to mirror the ISO definition with the exception that the first day of the week is Sunday instead of Monday. This means that the first day of the year is the Sunday prior to the first Thursday of the year.
There is no strftime flag to give a week number with this definition. But you can easily compute it with the C++20 <chrono> tools. Unfortunately they aren't shipping yet, but you can use this free, open-source C++20 chrono preview library which works with C++11/14/17.
In addition to computing week number, you'll also need to compute the year, as sometimes the gregorian year does not match the year associated with the week. For example according to https://www.calendar-365.com/2019-calendar.html, December 31, 2018 falls on week 1 of 2019.
So here is a function that computes both the year and the week number, given a date:
#include "date/date.h"
#include <chrono>
#include <iostream>
#include <utility>
// {year, week number}
std::pair<int, int>
outlook_weeknum(date::sys_days sd)
{
using namespace date;
auto y = year_month_day{sd + (Thursday - Sunday)}.year();
auto year_start = sys_days{Thursday[1]/January/y} - (Thursday - Sunday);
if (sd < year_start)
{
--y;
year_start = sys_days{Thursday[1]/January/y} - (Thursday - Sunday);
}
return {int{y}, (sd - year_start)/weeks{1} + 1};
}
The logic is a little tricky. The hard part is finding the first day of the year of the date, which is the Sunday prior to the first Thursday of the year. This is nominally:
auto year_start = sys_days{Thursday[1]/January/y} - (Thursday - Sunday);
where y is the year in the week numbering system (usually, but not always the same as the gregorian year). When the date is very late in the year i.e. December 28 - 31, it may fall in the first week of the next year. To catch that possibility, first bump the date by 4 days (the difference between Sunday and Thursday), and then compute the current year.
S M T W T F S
y-1 WL 21 22 23 24 25 26 27
y W1 28 29 30 31 1 2 3
After doing this, compute the start of the year. And if the start of the year happens to be after the date, then you are in the situation that your date belongs in the previous year. In this case, the week number year may be one less than the gregorian year. This can happen when Jan 1 is Friday or Saturday.
S M T W T F S
y-1 WL 27 28 29 30 31 1 2
y W1 3 4 5 6 7 8 9
In summary, the dates 12/28 - 12/31 can have a week year number either equal to their gregorian year, or one greater. And the dates 01/01 and 01/02 can have a week year number either equal to their gregorian year, or one lesser. -- All depending on what day of the month the first Thursday of January falls on [1 - 7].
Once the week number year (y) is figured out, then the week number is simply the difference between the date and the first of the year divided by 7 days (1 week), plus one to bias the first week to 1 instead of 0.
This can be exercised like this:
int
main()
{
using namespace date;
auto [i, w] = outlook_weeknum(2019_y/9/1);
std::cout << i << "-W" << w << '\n';
}
which outputs:
2019-W36
To port this code to C++20:
Drop the #include "date/date.h"
Change namespace date to namespace std::chrono
Change 2019_y to 2019y
My idea is to construct a string array with all the holidays. Then compare the input date with the array. Seems it quite inconvenient that have to type in all the weekend's dates etc. by hands. Any better ideas?
Using Howard Hinnant's free, open source C++11/14 date library, here are all the weekends in 2017.
#include "date.h"
#include <iostream>
int
main()
{
using namespace date;
for (sys_days sd = jan/1/2017; sd < jan/1/2018; sd += days{1})
{
weekday wd = sd;
if (wd == sat || wd == sun)
std::cout << sd << '\n';
}
}
2017-01-01
2017-01-07
2017-01-08
2017-01-14
...
2017-12-23
2017-12-24
2017-12-30
2017-12-31
My suggestion is to first determine the day of the week of the given date, to decide it to be Sunday or Saturday.
Use this logic to determine it: https://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week
For the code use this link: GeeksforGeeksCode
and the compare the days, to mark it as holiday or a working day.
You can set an initial day, like Jan 1st 2017, it's Sunday. And you can calculate if other days are workday or not.
For example, you have an array called days has 7 elements, stores Monday to Sunday. Like:
std::string days[] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
And when you want to know if a day is workday, let's say Jan 3rd 2017. First you can calculate difference between the day you want to calculate and initial day (which is Jan 1st 2017). In our case it is 2. Then the result is days[2 % 7 - 1] (-1 because our initial day is set to Sunday. If we set initial day as Monday, -1 is not necessary). Jan 3rd 2017 is Tuesday, that means it is a workday.
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;
}
using Qt 4.8 how can I print the time in the format DD HH SS? I have the seconds and I want to get back a string in that format.
QDateTime::fromTime_t(seconds).toString("ss hh DD");
see http://qt-project.org/doc/qt-5.0/qdatetime.html#toString
If you want a duration ( your question was really unclear) try something like :
QString seconds_to_DHMS(quint32 duration)
{
QString res;
int seconds = (int) (duration % 60);
duration /= 60;
int minutes = (int) (duration % 60);
duration /= 60;
int hours = (int) (duration % 24);
int days = (int) (duration / 24);
if((hours == 0)&&(days == 0))
return res.sprintf("%02d:%02d", minutes, seconds);
if (days == 0)
return res.sprintf("%02d:%02d:%02d", hours, minutes, seconds);
return res.sprintf("%dd%02d:%02d:%02d", days, hours, minutes, seconds);
}
Since you have the server uptime as seconds, you can use the QDateTime class.
QDateTime::fromTime_t(duration).toUTC().toString("dd hh ss");
Notice the toUTC, that's to set the beginning hour to 0. Since you will only be taking the date, hour and seconds, it doesn't really matter if the seconds are not since that date since the year won't be displayed.
You can use QDateTime::fromTime_t :
Returns a datetime whose date and time are the number of seconds that have passed since 1970-01-01T00:00:00, Coordinated Universal Time (Qt::UTC).
What you want to print is a duration of time...not a "moment" in clock time. QDateTime doesn't do much with durations except computing secsTo (and daysTo), and you pretty much have to roll your own printing.
Good news is the math isn't that hard:
Convert seconds to Days, Minutes and Seconds
Although your internationalization of words like seconds / days / years might be a nuisance. :(
The math is incredibly hard. Days are not 24 hours, they are usually 24 hours but sometimes 23 or 25 (daylight savings time changes) or 24 hours and a second or two (leap seconds). The same problem goes for months (obviously, since differently-sized months are common) years (leap day) and really anything that inherits day's problem by being defined in terms of days (weeks).
This is how im generating my date i want to add 1 year to it. Thanks in advance.
char tmpbuf[128];
time_t ltime;
struct tm *today;
stringstream reD;
string todayDate;
time( <ime );
today = localtime( <ime );
strftime( tmpbuf, 128,"%Y-%m-%d_%H:%M:%S", today );
reD << tmpbuf;
reD >> todayDate;
boost::replace_all(todayDate, "_", " ");
cout << todayDate << endl;
OK ive decided to go with boost since it will be easier to add days, so 2 examples i need one to add 1 year, and one to add 14 days, heres what i have so fare
#include "boost/date_time.hpp"
#include "boost/date_time/local_time/local_time.hpp"
using namespace boost::posix_time;
using namespace boost::local_time;
int main(){
local_date_time t = local_sec_clock::local_time(time_zone_ptr());
local_time_facet* lf(new local_time_facet("%Y-%m-%d_%H:%M:%S"));
std::cout.imbue(std::locale(std::cout.getloc(), lf));
std::cout << t << std::endl;
return 0;
}
Edit putting time into string
stringstream reD;
reD.imbue(locale(reD.getloc(), lf));
reD << t;
bthis = reD.str();
cout << bthis << endl;
If you're using C++, I highly recommend boost::date_time to take the leg-work out of this.
There is no such thing as "adding a year".
Let us suppose that you go into incrementing the year by 1, after all, that is what you are aiming for.
Unfortunately, there are some inconsistencies in the way we deal with the time:
leap years: if you are on February 29th, 2008, what does adding a year mean ? February 28th, 2009 or March 1st, 2009 ? (Hint: changing the month is very confusing for the user of a calendar)
leap seconds: on June 30th or December 31th, a second or two might be added to the last minute, making that minute 61 or 62 seconds. (Hint: once again, changing the day is confusing)
special events: like the calendar re-adjustment that occurred in 1582, where Thursday, October 4, 1582 was followed by Friday, October 15, 1582, resulting in a full 10 days loss.
The problem here, is not really in "adding" a year, you can always choose to round down (preferably when end-users are involved) or up. The real problem is that if you follow this reasoning, you unfortunately lose the symmetry between adding and removing a year:
original: February 29th, 2008
+ 1 year: March 1st, 2009 (rounding up)
- 1 year: March 1st, 2008
or by adding 4 years in several consecutive leaps:
original: February 29th, 2008
+ 2 years: February 28th, 2010 (rounding down)
+ 2 years: February 28th, 2012
Oups!
The mathematical solution to this is to simply evaluate the year duration in terms of seconds, Let's ask Wolfram about it: 3.154 × 10^7 seconds.
However, it may be quite confusing for the user.
And finally, the last solution is that whenever you make computations based on dates and duration, you save the original date away, compute the durations on their own, and then adjust the "displayed" date.
This way, you will be both mathematically correct (ie, respect symmetry and associativity) and behave intuitively for end users.
class MyTime {
...
private:
tm _origin;
tm _deviation;
};
However it is more work... so you have to decide on your scheme by yourself, depending on your application needs.
I agree about using boost::date_time, however, the solution here is quite easy.
today->tm_year++;
Although, if you happen to call localtime again, the value will be overwritten, so you should make a copy. Make today an instance instead of a pointer, and dereference the return value of localtime like this:
today = *localtime( <ime );
You'll have to take into account certain anomalies, like incrementing a year from February 29th on a leap year.
Edit: I see you've decided to use boost::date_time after all. This makes things much simpler. Here's how you add a year:
t += boost::gregorian::years(1);
And here's how you add 14 days:
t += boost::gregorian::days(14);
Or
t += boost::gregorian::weeks(2);
Oh good grief, you C++ people :)
// compare June 15 2018 - 2017 to 2017 - 2016
struct tm y1_tm, y2_tm, y3_tm;
time_t y1716, y1817; // differences
y1_tm.tm_sec = 0; // 2016
y1_tm.tm_min = 0;
y1_tm.tm_hour = 0;
y1_tm.tm_mon = 6;
y1_tm.tm_mday = 15;
y1_tm.tm_year = 2016 - 1900;
y1_tm.tm_mday = 1;
y2_tm.tm_sec = 0; // 2017
y2_tm.tm_min = 0;
y2_tm.tm_hour = 0;
y2_tm.tm_mon = 6;
y2_tm.tm_mday = 15;
y2_tm.tm_year = 2017 - 1900;
y2_tm.tm_mday = 1;
y3_tm.tm_sec = 0; // 2018
y3_tm.tm_min = 0;
y3_tm.tm_hour = 0;
y3_tm.tm_mon = 6;
y3_tm.tm_mday = 15;
y3_tm.tm_year = 2018 - 1900;
y3_tm.tm_mday = 1;
y1716 = mktime(&y2_tm) - mktime(&y1_tm); // 2017 - 2016
y1817 = mktime(&y3_tm) - mktime(&y2_tm); // 2018 - 2017
Both subtractions yield 31536000 seconds. Add that to a time_t for 1 year.