I am wrapping NodaTime for use in our software, and to be future-proof I thought I'd set up IWeekYearRule according to Bcl DateTimeFormat, like this:
var culture = CultureInfo.CurrentCulture;
var weekRule = WeekYearRules.FromCalendarWeekRule(
culture.DateTimeFormat.CalendarWeekRule,
culture.DateTimeFormat.FirstDayOfWeek);
Here in Norway we're following the following settings:
CalendarWeekRule.FirstFourDayWeek
DayOfWeek.Monday
However, much to my surprise, when I checked the week number of the following two dates, kalender.no disagrees:
2018-12-31: week 53 of 2018 (kalender.no says week 1 [implied: of 2019])
2019-01-01: week 1 of 2019 (kalender.no agrees here)
However, if I use WeekYearRules.Iso, they both return "week 1 of 2019", which is the correct one.
I have a bunch of questions but let's focus on the most immediate one:
How can I write the code to "future-proof" this part of the library, so that when we start rolling out the software into more parts of the world, it will calculate week numbers correctly out of the box, according to the local culture?
Or is this just not possible, because the culture information doesn't contain this?
Here's a LINQPad example that demonstrates NodaTime behavior (sorry for not including a [mcve] to begin with):
void Main()
{
"-------------- ISO --------------".Dump();
Test(WeekYearRules.Iso);
"-------------- BCL --------------".Dump();
Test(WeekYearRules.FromCalendarWeekRule(CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday));
}
public static void Test(IWeekYearRule rule)
{
var date = new LocalDate(2018, 12, 31);
$"{date}, {rule.GetWeekOfWeekYear(date)} in {rule.GetWeekYear(date)}".Dump();
date = new LocalDate(2019, 1, 1);
$"{date}, {rule.GetWeekOfWeekYear(date)} in {rule.GetWeekYear(date)}".Dump();
}
Output:
-------------- ISO --------------
mandag 31. desember 2018, 1 in 2019
tirsdag 1. januar 2019, 1 in 2019
-------------- BCL --------------
mandag 31. desember 2018, 53 in 2018
tirsdag 1. januar 2019, 1 in 2019
(and just to be clear, the ISO output is the correct one for Norway)
This is NodaTime 2.4.4, Windows 10, .NET 4.7.2.
And here's a LINQPad example without using NodaTime:
void Main()
{
var culture = CultureInfo.GetCultureInfo("nb-NO");
var calendarWeekRule = culture.DateTimeFormat.CalendarWeekRule.Dump();
var firstDayOfWeek = culture.DateTimeFormat.FirstDayOfWeek.Dump();
var date = new DateTime(2018, 12, 31);
$"{date.DayOfWeek} {date}, week {culture.Calendar.GetWeekOfYear(date, calendarWeekRule, firstDayOfWeek)}".Dump();
date = new DateTime(2019, 1, 1);
$"{date.DayOfWeek} {date}, week {culture.Calendar.GetWeekOfYear(date, calendarWeekRule, firstDayOfWeek)}".Dump();
}
Output:
FirstFourDayWeek
Monday
Monday 31.12.2018 00.00.00, week 53
Tuesday 01.01.2019 00.00.00, week 1
The Monday there is in the same week as week 1 of 2019, and is counted as week 1 in 2019 here in Norway, so the first of the two dates should really have been:
Monday 31.12.2018 00.00.00, week 1
Related
I have a web application where the user will input 2 dates. A StartDate and an EndDate. Now I want to write it so that when StartDate and EndDate is selected to determine how many weeks there are and then to display the dates of those weeks for example if the user selects 01-11-2018 as the StartDate and 31-12-2018 as the EndDate then I want to display the following and keep in mind the Weeks are just for reference as to how it is going to look:
Week 98 : 01 November 2018 - 03 November 2018
Week 99 : 04 November 2018 - 10 November 2018
Week 100 : 11 November 2018 - 17 November 2018
I already have the amount of weeks by using this previous post.
Now I just want to be able to to display each individual weeks Start and End date in all the weeks. I tried creating a list with the weeks and then using a Foreach to check the weeks added but this is not quite right. I am just searching for the most efficient way of accomplishing this goal.
Links also checked with similar problems are :
Link1
I have made this snippet... not sure if everything is up to spec:
var startDate = new DateTime(2018, 11, 1);
var endDate = new DateTime(2018, 12, 31);
int diff = (7 + (startDate.DayOfWeek - DayOfWeek.Monday)) % 7;
var weekStartDate = startDate.AddDays(-1 * diff).Date;
var i = 1;
var weekEndDate = DateTime.MinValue;
while(weekEndDate < endDate) {
weekEndDate = weekStartDate.AddDays(6);
var shownStartDate = weekStartDate < startDate ? startDate : weekStartDate;
var shownEndDate = weekEndDate > endDate ? endDate : weekEndDate;
Console.WriteLine($"Week {i++}: {shownStartDate:dd MMMM yyyy} - {shownEndDate:dd MMMM yyyy}");
weekStartDate = weekStartDate.AddDays(7);
}
This assumes your weeks are "counting", starting on the week the start date is in, and uses monday as the first day of week and sunday as the last one (the ranges you see are monday - sunday except for the first/last week, which would use the start/end date instead if it doesn't happen to be monday or sunday)
You can run it online here: https://dotnetfiddle.net/jJ4Ydu
If you also need to know which week of the year it is, then it depends if you want the .NET style or the ISO8601 style... the typical one is the latter, and you can use, for example, the method found on this answer, so it'd look something like: https://dotnetfiddle.net/oJscjF
Notice how Dec-31st-2018 (which is monday) is the week 1 of 2019 on ISO8601, but week 53 for .NET
I'm trying to figure out the format of few dates that are available in numeric format. I need to convert them to exact dates. I have a sample dates that I was trying to convert:
1443506173.0 >> Sep 29, 2015
1443505895.0 >> Sep 29, 2015
1441805416.0 >> Sep 09, 2015
1438174556.0 >> Jul 29, 2015
1436476814.0 >> Jul 10, 2015
1414994162.0 >> Nov 03, 2014
1413294207.0 >> Oct 14, 2014
By looking at the first two entries, I can see that the numbers are changing but both are representing the same dates. Means there must be time embedded into this date format. Currently I'm concerned with extracting only date, I dont need to extract time at the moment. Would be great if it was simple enough to extract time as well.
Can anyone help figure this out? In case you're wondering, I got these dates from Instagram posts feed. Using WebClient I downloaded an Instagram photo's URL. This date format is there in the scripts section. If I'm able to decode this date format, I would know what the date (and time) of the photo post was at Instagram.
Thanks in advance. I'm using C# to perform this conversion.
Instagram uses the Unix Timestamp for its format (the number of seconds since 1st January 1970).
One method of converting this to a DateTime object would be:
DateTime dateTime = new DateTime(1970,1,1,0,0,0,0,System.DateTimeKind.Utc);
dateTime = dateTime.AddSeconds(yourUnixTimestampValue).ToLocalTime();
This is a UNIX Timestamp
void Main()
{
var timestamps = new[]{
new {stamp = 1443506173.0, datetime = new DateTime(2015, 9, 29)},
new {stamp = 1443505895.0, datetime = new DateTime(2015, 9, 29)},
new {stamp = 1413294207.0, datetime = new DateTime(2014, 10, 14)}
};
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
foreach (var i in timestamps)
{
Console.WriteLine("Item: {0}, Converted: {1}", i, new DateTime(1970, 1,1).AddSeconds(i.stamp).ToLongDateString());
}
}
Output:
Item: { stamp = 1443506173, datetime = 09/29/2015 00:00:00 }, Converted: Tuesday, 29 September 2015
Item: { stamp = 1443505895, datetime = 09/29/2015 00:00:00 }, Converted: Tuesday, 29 September 2015
Item: { stamp = 1413294207, datetime = 10/14/2014 00:00:00 }, Converted: Tuesday, 14 October 2014
I have following code to get the weeknumber of the year given in the DateTime object Time
public static int WeeksInYear(DateTime date)
{
GregorianCalendar cal = new GregorianCalendar(GregorianCalendarTypes.Localized);
return cal.GetWeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
}
I give the function the date 1/1/2012 which should return week 1, but it is returning week 52. And I can't seem to figure out why. Anyone have an idea why?
The algorithm is doing exactly what you have instructed it to do. You have the CalanderWeekRule set to FirstFourDayWeek. The 1st of January 2012 was not part of the first four day week, so you have instructed the calander to start counting from January 2nd.
Calculate date from week number
public static int WeeksInYear(DateTime date)
{
GregorianCalendar cal = new GregorianCalendar(GregorianCalendarTypes.Localized);
return cal.GetWeekOfYear(date, CalendarWeekRule.FirstDay, DayOfWeek.Monday);
I think if change CalendarWeekRule.FirstFourDayWeek to CalendarWeekRule.FirstDay then this will work fine.
I changed this then its working fine.
The weeknumber was correctly calculated. You should have a read on how weeknumbers are actually calculated/counted (Wikipedia?!).
Attention: The built-in calculation of week-number-calculation is buggy. Microsoft describes the problem in the KnowledgeBase-Article 200299. It has problems with ISO-8601.
You can use the class Week of the Time Period Library for .NET which supports supports the ISO 8601 week numbering:
TimeCalendar calendar = new TimeCalendar(
new TimeCalendarConfig { YearWeekType = YearWeekType.Iso8601 } );
Week week = new Week( new DateTime( 2012, 01, 01 ), calendar );
Console.WriteLine( "week #: ", week.WeekOfYear );
I assume that because 1/1/2012 was a Sunday and GetWeekOfYear says it returns the week which includes the date, it's returning the last week of 2011 rather than the first week of 2012.
Have a look at the CalendarRule for clarification.
1/1/2012 it was sunday. I believe that's why you get 52, because it was last day of the last year week. For the 2nd of january you should get the right result.
I'm trying to call something every 3 months (quarterly) in Quartz.NET (using both stable and latest version 2 which is beta with same results).
I create cron trigger with 0 30 8 3 */3 ? * to be called every 3 months at 8.30am on third of the month it occurs.
So technically since its 2 of September today I would expect it to trigger tomorrow. However it next run time shows as being next month. Why is that so?
Updated: As per answers I got I created following method - could be useful for someone:
public static string CalculateMonthsWithInterval(int startMonth, int interval)
{
var months = new List<string>();
var monthNames = new [] {"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};
var monthSelector = startMonth % interval;
for (var i = 0; i < 12; i++)
{
if (i % interval == monthSelector)
{
months.Add(monthNames[i]);
}
}
return string.Join(",", months.ToArray());
}
Ps: I didn't use indexes for months because for some reason it wasn't working well with my Quartz (v2 BETA). Also its easier to read in DB level.
Example call - Every 3 months based on startDate:
var cronMonths = CronUtils.CalculateMonthsWithInterval((startDate.Month - 1), 3);
Well I think that's because the scheduler will verify which month can be divided by 3, since all month in Quartz are based 0 (according to: http://www.quartz-scheduler.org/documentation/quartz-2.x/tutorials/tutorial-lesson-06), the month that will be scheduled will be january, april, july and october.
0 mod 3 = 0 -> JAN
1 mod 3 = 1 -> FEB
...
8 mod 3 = 2 -> SEP
9 mod 3 = 0 -> OCT
The Quartz scheduler will analyse your cron expression and keep only those where their modulus 3 equals to 0.
If you want it to be 1 month before that (march, june, september and october) you will have to set it to:
0 30 8 3 MAR,JUN,SEP,DEC ? *
A good page to create cron expressions: http://www.cronmaker.com/
Cron format:
0 0 12 1 1/3 ? *
Executes every:
1. Saturday, April 1, 2017 12:00 PM
2. Saturday, July 1, 2017 12:00 PM
3. Sunday, October 1, 2017 12:00 PM
4. Monday, January 1, 2018 12:00 PM
5. Sunday, April 1, 2018 12:00 PM
So I have an application that needs to get a date focus so it can run appropriately. Given a particular date to focus on it needs to know what week it is in. I'm calculating weeks based on Monday dates. And I'm wondering if my focus on Mondays is excessive.
public static DateTime PreviousMonday(this DateTime dt)
{
var dateDayOfWeek = (int)dt.DayOfWeek;
if (dateDayOfWeek==0)
{
dateDayOfWeek = dateDayOfWeek + 7;
}
var alterNumber = dateDayOfWeek - ((dateDayOfWeek*2)-1);
return dt.AddDays(alterNumber);
}
/// <summary>
/// Personal tax week starts on the first Monday after the week with 6th April in unless 6th April is a Monday in
/// which case that starts the first week. In a leap year this means you can have a week 53 which due to the mod 4 approach of calculating
/// flexi week means you get a 5 week flexi period.
/// As such this method forces the weeks into the range 1 - 52 by finding the week number for the week containing 6th April and
/// the number for the current week. Treating the 6th April week as week 1 and using the difference to calculate the tax week.
/// </summary>
public static int GetTaxWeek(this DateTime dt)
{
var startTaxYear = GetActualWeekNumber(new DateTime(DateTime.Now.Year, 4, 6));
var thisWeekNumber = GetActualWeekNumber(dt);
var difference = thisWeekNumber - startTaxYear;
return difference < 0 ? 53 + difference : difference + 1;
}
private static int GetActualWeekNumber(DateTime dt)
{
var ci = System.Threading.Thread.CurrentThread.CurrentCulture;
var cal = ci.Calendar;
var calWeekRule = ci.DateTimeFormat.CalendarWeekRule;
var fDoW = ci.DateTimeFormat.FirstDayOfWeek;
return cal.GetWeekOfYear(dt, calWeekRule, fDoW);
}
public static int PeriodWeek(this DateTime dt)
{
var rawPeriodWeek = GetTaxWeek(dt) % 4;
return rawPeriodWeek == 3 ? 1 : rawPeriodWeek + 2;
}
}
The system runs a rolling 4 week schedule starting in the first tax week and needs to behave differently depending on where in the schedule it is. So you can see...
Get a date from a user (say userDate)
Call userDate=userDate.PreviousMonday();
to get to the Monday of the week
given - where Sunday is the week end
Call userDate.PeriodWeek(); and get
the Period you are in from 1 to 4.
GetTaxWeek is public because it is used elsewhere... I also replace the date as it is used more than once and I don't want to have to remember to change it more than once.
Can I see the wood for the trees? Or is there a more error free way of doing this.
I think you can greatly simplify your code using the GregorianCalendar inside System.Globalization. Here you can get the week number for a given date like this:
GregorianCalendar gc = new GregorianCalendar();
int weekno = gc.GetWeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
You see here that you can give the rules for how to caclulate the week number according to your local rules. Like here in Norway, we have Monday as our first week day, and the first week of the year is the first week that has four or more days. Set this to your culture specific rules to get the correct week numbers.
Some of your specific handling you still ahve to do by hand, but some of the clutter can be removed using this at least :)
why are you not using a DateTimePicker control? it will tell you the day for the user selected date. Then you can simply subtract no. of days from it to get date for monday. For example:
I'm using a DateTimePicker control and named it dtpTemp. the event used is
dtpTemp_ValueChanged()
dtpTemp.Value.DayOfWeek - will give you the day: tuesday, wednesday, thursday etc.
then you can use following code with switch case accordingly:
dtpTemp.Value.AddDays(num); to get date for monday
here num will have -ve values which will depend on day calculated above. Values: -1 for tuesday, -2 for wednesday, -3 for thursday and so on.
plus, using a datetimepicker will also have a positive impact on the UI itself.