Locale dependent Week of Year in C/C++ - c++

I am trying to get Week of Year. For this I am using tm * __CRTDECL localtime(const time_t * _Time) but I am not getting desired result which should be locale dependent. So I was looking for solution and some more information. I have found that there is API in JAVA Calendar.getInstance(Locale.GERMAN);.So I am just wondering whether there is such API in c/c++ as I did not find any ( not sure ). If not can anybody give me some time pointers to get Week of Year locale dependant.
The code I am using
int MyClass::getCalendarWeek(time_t time, int * p_year)
{
// Get tm structure of time parameter
tm* pCurrentTm = localtime(&time);
// determine Thursday in that week
LONG offSet = 4 - pCurrentTm->tm_wday;
if (offSet >= 4) {
offSet = -3; // Sunday
}
time += offSet * 86400L;
pCurrentTm = localtime(&time);
if (p_year) {
*p_year = pCurrentTm->tm_year + 1900;// year of current calendar week
}
return (pCurrentTm->tm_yday + 7) / 7; // current calendar week
}
Output:
11 July is in week 28 when running most locales that have monday as first day of the week, but it is week 29 for sunday as first day of week.

Related

strftime - convert week number ISO8601 to Gregorian standard C++

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

Solution doesn't work for number of days between two dates

I know this question has been asked a few times, and I'm asking again because I've got issues with existing solutions on SO.
My goal is to find number of days between 1900-01-01 and a given date. The date will be in the format as yyyy-mm-dd and the type is std::string.
The solution I've followed is https://stackoverflow.com/a/14219008/2633803
And below is my version:
std::string numberOfDaysSince1900v2(std::string aDate)
{
string year, month, day;
year = aDate.substr(0, 4);
month = aDate.substr(5, 2);
day = aDate.substr(8, 2);
struct std::tm a = { 0,0,0,1,1,100 }; /* Jan 1, 2000 */
struct std::tm b = { 0,0,0,std::stoi(day),std::stoi(month),std::stoi(year) - 1900 };
std::time_t x = std::mktime(&a);
std::time_t y = std::mktime(&b);
double difference;
if (x != (std::time_t)(-1) && y != (std::time_t)(-1))
{
difference = std::difftime(y, x) / (60 * 60 * 24) + 36526; //36526 is number of days between 1900-01-01 and 2000-01-01
}
return std::to_string(difference);
}
It worked fine until the given date comes to 2019-01-29 and 2019-02-01. In both cases, the output is 43494. And for the whole Feb, the output is 3 days less than expected. Then, when it comes to March 2019, the output is back to normal again.
Another case is 2019-09-03, the output is 43710, whilst the expected output is 43711.
Why would this happen to these specific dates? I ran the solution step by step and closely watched the variables in the memory but couldn't explain it.
Any suggestion is appreciated. Thanks.
The month should be represented as an integer between 0 and 11, not 1 and 12.
So
struct std::tm a = { 0,0,0,1,0,100 }; /* Jan 1, 2000 */
struct std::tm b = { 0,0,0,std::stoi(day),std::stoi(month)-1,std::stoi(year) - 1900 };
I would say there are other problems with your code. You cannot reliably initialise a tm like that (the order of fields within the struct is not guaranteed). Neither does difftime necessarily return a number of seconds (which you are assuming).

Date formatting - adding days to date

I have date in this format for example :
12.2.2015
I have to add days to that date. For example i have to add to that date + 1000 days so the output will not be 12 . 2 . 2015 but 14 . 3 . 2018 ( it's not accurate i did not calculate it). Is there any effective algorithm for this problem or do i have to loop it and make lots of conditions about leap year etc?
So let's say you have
int year = 2015;
int month = 2;
int day = 12;
int offset = 1000;
You can utilize <ctime> functions like mktime and localtime and belief that every day has 86400 seconds to do what you need. Something like this.
#include <ctime>
struct tm orig_date;
orig_date->tm_sec = 0;
orig_date->tm_min = 0;
orig_date->tm_hour = 12; /* midday, so we make space for non-86400 errors */
orig_date->tm_mday = day;
orig_date->tm_mon = month - 1; /* uses 0-11 range */
orig_date->tm_year = year;
orig_date->tm_isdst = 0;
time_t utc = mktime(&orig_date);
utc += (offset * 86400);
struct tm *new_date;
new_date = localtime(&utc);
printf("Old date: %d. %d. %d\n", orig_date.tm_mday, orig_date.tm_mon, orig_date.tm_year);
printf("New date: %d. %d. %d\n", new_date->tm_mday, new_date->tm_mon, new_date->tm_year);
The code will produce following (which I checked to be correct):
Old date: 12. 1. 2015
New date: 8. 10. 2017

Find the date given the year, the month and the "nth" occurrance of day within the month C/C++

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;
}

How to create arbitrary date and add days to it - c++

I am trying to write an application for an assignment and I am new to c++. A small portion of the application requires me to store a date and add an arbitrary number of days as an offset from the date. I know how I would accomplish this with Java or C# but I have been unable to find anything for c++. My professor alluded to ctime but after many searches all the examples I found had to do with the current system time. How do I create a ctime::tm struct and set it to an arbitrary date? Is it possible to add a number of days using ctime to obtain another date? For example, if I added 40 days to January 1, 2001 I would expect February 10, 2001 not January 41, 2001.
To be an example of usage
#include <stdio.h>
#include <time.h>
int main ()
{
time_t currentTime;
time(&currentTime);
struct tm * tmDate;
int day, month, year;
tmDate = localtime (&currentTime);
tmDate->tm_year = 99;
tmDate->tm_mon = 11;
tmDate->tm_mday = 10;
mktime ( tmDate );
printf("now: %d-%d-%d %d:%d:%d\n", tmDate->tm_year + 1900, tmDate->tm_mon + 1, tmDate->tm_mday, tmDate->tm_hour, tmDate->tm_min, tmDate->tm_sec);
return 0;
}
as you can see on
tmDate->tm_year = 99;
tmDate->tm_mon = 11;
tmDate->tm_mday = 10;
you can set, sub, add months, years, days .. to date.
For example simply you can add 1 month to date with
tmDate->tm_mon++;