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
Related
How will I get the expiration date of an item, which is based on week code? Whenever I run the code that I made, the program reads the current date and disregards the week code. For example:
Week Code: 2138 (2021 week 38)
Shelf_life : 6 months
CTime weekcode = CTime::GetCurrentTime();
CTimeSpan shelf_no = CTimeSpan(atoi(view->m_pODBCPartsNo->m_shelf_life), 0, 0, 0);
CTime expiration_date = weekcode = shelf_no;
Week code is a date code, for example 2138, year 2021(21) week 38(38). Week 38 is around September 19, 2021 to September 25, 2021.
Now the question is, how will i get the expiration date that is based on week code ? Will I still use "GetCurrentTime" ?
Here's how I would do it using Howard Hinnant's free, open-source, header-only date library:
#include "date/iso_week.h"
#include "date/date.h"
#include <chrono>
#include <iostream>
int
main()
{
using namespace date;
int weekcode = 2138;
int shelf_life = 6;
iso_week::year y{weekcode / 100 + 2000};
iso_week::weeknum wk(weekcode % 100);
auto prod_date = y/wk/iso_week::wed;
auto exp_date = floor<days>(sys_days{prod_date} + months{shelf_life});
std::cout << "Production date : " << prod_date << '\n';
std::cout << "Expiration date : " << exp_date << '\n';
}
This assumes that you are using ISO week date as the definition of "week number", which is an internationally accepted standard. The program begins by simply decoding weekcode into a year and weeknum. It then uses the iso_week library to create a production date with this year and weeknum. To complete this date, a weekday must be supplied. I've chosen Wednesday since this is the middle of the ISO work week.
auto prod_date = y/wk/iso_week::wed;
The expiration date can then be computed by simply adding 6 months to that date. Note that this addition is done using a chronological computation because product aging is a physical process that does not care that different months have different lengths. Using an "average month" length is adequate.
To do this chronological arithmetic, the production date must first be converted to a chronological date, sys_days. This is simply a count of days since the Unix Time epoch.
auto exp_date = floor<days>(sys_days{prod_date} + months{shelf_life});
The result has a precision finer than days since the average month duration is not an integral number of days. So it must be truncated back to the precision of days to create a date, as opposed to a date-time.
The above program prints out:
Production date : 2021-W38-Wed
Expiration date : 2022-03-23
Need a C++ function to find out the date of the first day of week from the week number.
Input : year and week number
Output : date [It should be 1st day of that week number]
e.g :
inputs :
year – 2017, week number – 8
Output: 20th Feb 2017
inputs:
year – 2017, week number – 10
Output: `6th March 2017
Using Howard Hinnant's free, open-source, header-only date library, it can look like this:
#include "date.h"
#include "iso_week.h"
#include <iostream>
int
main()
{
using namespace iso_week::literals;
std::cout << date::year_month_day{2017_y/8_w/mon} << '\n';
std::cout << date::year_month_day{2017_y/10_w/mon} << '\n';
}
which outputs:
2017-02-20
2017-03-06
There are also getters for year, month and day on the year_month_day types, and plenty of formatting options.
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;
}
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.