For my Fiscal Calendar in Power BI I am currently trying to implement the 4-4-5 approach.
Our calendar works with 4-4-5 week quarters. Since this only has 364 days each year, there must be a 53 week year after a few years. As a result, December has 6 instead of 5 weeks.
Unfortunately, there is still no approach based on DAX.
In another post here, however, I found a JAVA code, which probably determines whether the year has 53 weeks or not:
calculate number of weeks in a given year
private static long getNumberOfWeeksInYear(LocalDate date) {
LocalDate middleOfYear = date.withDayOfMonth(1).withMonth(6);
return middleOfYear.range(WeekFields.ISO.weekOfWeekBasedYear()).getMaximum();
}
public static void main(String[] args) {
for (int year = 2000; year < 2400; year++) {
long numberOfWeeks = getNumberOfWeeksInYear(LocalDate.of(year, 1, 1));
if (numberOfWeeks != 52) {
System.out.println(year + " has " + numberOfWeeks + " weeks");
}
}
}
Do any of you know how to translate the code into Dax?
Our Fiscal Calendar starts not based on the gregorian calendar.
This year starts at 30.12.19 and ends 03.01.21. This year has 53 weeks.
I cannot help with PowerPivot and DAX. But I can tell you how to get those information usingExcel formulas.
Given the year in A2 you can calculate the Monday of the first ISO calendar week in that year using following formula:
=DATE($A2,1,1)-WEEKDAY(DATE($A2,1,1),3)+(ISOWEEKNUM(DATE($A2,1,1)-WEEKDAY(DATE($A2,1,1),3))<>1)*7
You can calculate the Sunday of the last ISO calendar week in that year using following formula:
=DATE($A2+1,1,1)-WEEKDAY(DATE($A2+1,1,1),3)+(ISOWEEKNUM(DATE($A2+1,1,1)-WEEKDAY(DATE($A2+1,1,1),3))<>1)*7-1
Given the Sunday of the last ISO calendar week placed in C2, following formula calculates the number of ISO weeks of that year:
=ISOWEEKNUM($C2)
Example:
As you see, the years are from A2 downwards. Formula to calculate the Monday of the first ISO calendar week is placed in B2 downwards. Formula to calculate the Sunday of the last ISO calendar week is in C2 downwards. And the formula to calculate the number of ISO weeks of that year is in D2 downwards.
Related
I need to get the 4-5-4 Calendar Week from a Date. Is there any utility like Georgian Calendar in Java for 4-5-4 Retail Calendar?
If not, how can I create one? What all logic is needed? What is 53rd Week in case of Leap Year?
For example, if I pass a date (DD-MM-YYY) 04-03-2018 as input I should get March Week 1 as output.
Or, if I give 01-04-2018 as input I should get March Week 5 as output.
Please help me by providing a way to build this utility.
The following class should do it:
public class NrfMonthWeek {
public static NrfMonthWeek getWeek(LocalDate date) {
// Determine NRF calendar year.
// The year begins on the Sunday in the interval Jan 29 through Feb 4.
LocalDate firstDayOfNrfYear = date.with(MonthDay.of(Month.JANUARY, 29))
.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY));
if (date.isBefore(firstDayOfNrfYear)) { // previous NRF year
firstDayOfNrfYear = date.minusYears(1)
.with(MonthDay.of(Month.JANUARY, 29))
.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY));
}
// 1-based week of NRF year
int weekOfNrfYear = (int) ChronoUnit.WEEKS.between(firstDayOfNrfYear, date) + 1;
assert 1 <= weekOfNrfYear && weekOfNrfYear <= 53 : weekOfNrfYear;
YearMonth firstMonthOfNrfYear = YearMonth.from(firstDayOfNrfYear)
.with(Month.FEBRUARY);
if (weekOfNrfYear == 53) {
// Special case: the last week of a 53 weeks year belongs to
// the last month, January; this makes it a 5 weeks month.
return new NrfMonthWeek(firstMonthOfNrfYear.plusMonths(11), 5);
} else {
// 1-based month of NRF year (1 = February through 12 = January).
// A little math trickery to make the 4-5-4 pattern real.
int monthOfNrfYear = (weekOfNrfYear * 3 + 11) / 13;
// Number of weeks before the NRF month: 0 for February, 4 for March, 9 for April, etc.
int weeksBeforeMonth = (monthOfNrfYear * 13 - 12) / 3;
int weekOfMonth = weekOfNrfYear - weeksBeforeMonth;
return new NrfMonthWeek(
firstMonthOfNrfYear.plusMonths(monthOfNrfYear - 1), weekOfMonth);
}
}
private YearMonth month;
/** 1 through 5 */
private int weekOfMonth;
public NrfMonthWeek(YearMonth month, int weekOfMonth) {
this.month = Objects.requireNonNull(month);
if (weekOfMonth < 1 || weekOfMonth > 5) {
throw new IllegalArgumentException("Incorrect week number " + weekOfMonth);
}
this.weekOfMonth = weekOfMonth;
}
#Override
public String toString() {
return month.getMonth().getDisplayName(TextStyle.FULL, Locale.US)
+ " Week " + weekOfMonth;
}
}
Let’s try it. Here I pass the two dates from your question to the getWeek method:
System.out.println(NrfMonthWeek.getWeek(LocalDate.of(2018, Month.MARCH, 4)));
System.out.println(NrfMonthWeek.getWeek(LocalDate.of(2018, Month.APRIL, 1)));
This prints the desired:
March Week 1
March Week 5
Though only month and week are printed, also the year is contained in the object returned from getWeek.
The formulas for calculating the month and week-of-month are cryptic. I have no really good argument why they work, though such an argument could probably be constructed. I have tested them with all relevant values, and you are free to do the same. Other than that, using java.time, the modern Java date and time API, it wasn’t too bad.
If that were me, I would have finer validation in the NrfMonthWeek constructor, only allowing week 5 in the months that may have 5 weeks. I am leaving that to you. And I would have a pretty thorough unit test.
Please check whether my understanding agrees with yours: If I have understood correctly from the example calendars that Basil Bourque linked to in his answer, the NRF 4-5-4 year starts with February. Its weeks begin on Sunday, and the first week of the year is the first week that contains at least 4 days of February. In other words, the week that contains February 4. In yet other words, the week that begins on a Sunday in the interval January 29 through February 4. Months March, June, September and December always have 5 weeks. In case of a 53 weeks year also January has 5 weeks.
No support built-in
Neither the modern java.time classes nor the legacy date-time classes (Date/Calendar) directly support the National Retail Federation 4-5-4 Calendar.
Implement Chronology
I suspect the best way to solve this problem is to implement a Chronology for the java.time framework.
Java 8 and later bundle five implementations (HijrahChronology, IsoChronology, JapaneseChronology, MinguoChronology, ThaiBuddhistChronology). Their source is available in the OpenJDK project.
The ThreeTen-Extra project provides ten more chronologies (AccountingChronology, BritishCutoverChronology, CopticChronology, DiscordianChronology, EthiopicChronology, InternationalFixedChronology, JulianChronology, PaxChronology, Symmetry010Chronology, Symmetry454Chronology) whose source code might help.
I'm trying to get the number of the week for a date , In my country the week begins on Sunday so the week number of 6/5/2016 is 23 but it returning 22 because the ISO week in JAVA starts from Monday , I have used the following methods but it's not working
mCalendar = Calendar.getInstance();
int weekNum = mCalendar.get(Calendar.WEEK_OF_YEAR); //returns 22 I need 23
// I have tried the following method but it has no effect
mCalendar.setFirstDayOfWeek(Calendar.SUNDAY);
note that I can't use the Time Class I can only use Java 7
Java 8 version full answer:
public<T extends Temporal> long getWeekNumber(T tObj) {
DayOfWeek firstDayOfWeek = DayOfWeek.SUNDAY; // set your
Temporal firstDayOfThisYear = tObj.with(TemporalAdjusters.firstDayOfYear());
Temporal firstDayOfWeekInMonth = firstDayOfThisYear
.with(TemporalAdjusters.dayOfWeekInMonth(1, firstDayOfWeek));
return ChronoUnit.WEEKS.between(tObj, firstDayOfWeekInMonth);
}
The parameter can be any Temporal type, even the Temporal itself.
I don't know from where you are but Java has an awesome Calendar which allows the following:
Calendar calendar = Calendar.getInstance(Locale.TRADITIONAL_CHINESE);
int weekNumber = calendar.get(Calendar.WEEK_OF_YEAR);
System.out.println("Number of week: " + weekNumber);
// produces 24
Calendar calendar = Calendar.getInstance(Locale.UK);
int weekNumber = calendar.get(Calendar.WEEK_OF_YEAR);
System.out.println("Number of week: " + weekNumber);
// produces 22
You could use the locale constants to specify your location and i think you will get the right number of weeks.
Edit:
Now I see the failure in your code. Please note that Java works from the top to the button of your code:
Calendar calendar = Calendar.getInstance();
// First set the first day of the week ...
calendar.setFirstDayOfWeek(Calendar.SUNDAY);
// ... and than you could ask the calendar for the week
int weekNumber = calendar.get(Calendar.WEEK_OF_YEAR);
// will produce 23
System.out.println("Number of week: " + weekNumber);
I don't know where you are from.
I'm french and that works perfectly this way :
Calendar mCalendar = Calendar.getInstance(Locale.FRANCE);
mCalendar.setFirstDayOfWeek(Calendar.SUNDAY);
int weekNum = mCalendar.get(Calendar.WEEK_OF_YEAR);
System.out.println(weekNum); // --> 23
Get the current moment.See this Question: How to initialize a variable of type date in Java?
Determine the first Sunday of the year.Handled thoroughly in this question: How to get all the Sunday's of a year
Count weeks between that first Sunday and current moment.See the Question: Get the number of weeks between two Dates
I've just figured out how to change it
you need to set up two things
1-first day of the week
2-the minimal day of week
setFirstDayOfWeek(Calendar.SUNDAY);
setMinimalDaysInFirstWeek(7);
this will tell the calendar to make the fist day is sunday and with 7 days minimal week
My team is looking to switch from Joda time to java.time, but we're seeing different behavior in formatting using the same pattern. The issue arises when we're using the week-of-week-year w symbol:
final String dateString = "2016-01-04 00:00:00";
final String inputPattern = "yyyy-MM-dd HH:mm:ss";
// parse the input string using Joda
final org.joda.time.format.DateTimeFormatter jodaInputFormatter = org.joda.time.format.DateTimeFormat.forPattern(inputPattern);
final org.joda.time.DateTime jodaDateTime = jodaInputFormatter.parseDateTime(dateString);
// parse the input string using two different java.time classes
final java.time.format.DateTimeFormatter javaTimeInputFormatter = java.time.format.DateTimeFormatter.ofPattern(inputPattern).withZone(java.time.ZoneOffset.UTC);
final java.time.LocalDateTime localDateTime = java.time.LocalDateTime.parse(dateString, javaTimeInputFormatter);
final java.time.ZonedDateTime zonedDateTime = java.time.ZonedDateTime.parse(dateString, javaTimeInputFormatter);
final String outputPattern = "'week' w - dd/MM/yyyy HH:mm:ss";
final org.joda.time.format.DateTimeFormatter jodaOutputFormatter = org.joda.time.format.DateTimeFormat.forPattern(outputPattern);
final java.time.format.DateTimeFormatter javaTimeOutputFormatter = java.time.format.DateTimeFormatter.ofPattern(outputPattern);
// output: week 1 - 04/01/2016 00:00:00
System.out.println("With joda: " + jodaOutputFormatter.print(jodaDateTime));
// output: week 2 - 04/01/2016 00:00:00
System.out.println("With LocalDateTime: " + javaTimeOutputFormatter.format(localDateTime));
// output: week 2 - 04/01/2016 00:00:00
System.out.println("With ZonedDateTime: " + javaTimeOutputFormatter.format(zonedDateTime));
For some reason, the output from the w symbol is off-by-one across the two implementations.
What is causing this inconsistency? Is the w symbol inconsistently implemented across Joda time and java.time?
Well, it is a little bit speculative, but since you told me that your system timezone is EST (-05:00) I assume that you are sitting in US (New York?). And US does not apply ISO-8601-week rules. Weeks start on Sunday, and the first week of the year does not need to contain at least 4 days (even one day is enough to be counted as first week of year).
So let's look at your example date of 4th of January. It is a Monday. The first US-week is from 2016-01-01 until 2016-01-02 (2 days - enough for US). And the second US-week starts on Sunday the 3rd of January, so the fourth of January is in the second week, too.
And now the critical point: java.time (JSR-310) uses a localized week of week-based-year for the pattern symbol w, see also its backport which should have the same code. Code excerpt:
} else if (cur == 'w') {
if (count > 2) {
throw new IllegalArgumentException("Too many pattern letters: " + cur);
}
appendInternal(new WeekFieldsPrinterParser('w', count));
...
static final class WeekFieldsPrinterParser implements DateTimePrinterParser {
private final char letter;
private final int count;
public WeekFieldsPrinterParser(char letter, int count) {
this.letter = letter;
this.count = count;
}
#Override
public boolean print(DateTimePrintContext context, StringBuilder buf) {
WeekFields weekFields = WeekFields.of(context.getLocale());
DateTimePrinterParser pp = evaluate(weekFields);
return pp.print(context, buf);
}
The use of WeekFields.of(context.getLocale()) for the pattern symbol "w" is evident.
In contrast, Joda-Time only uses ISO-8601-week-definition which let weeks start on Monday and count that week as first week of year which contains at least four days in current calendar year. So the Monday 4th of January is the start of the first week-of-year because the three days before are not enough for ISO-8601 to be counted as week. Those preceding days are instead considered as last week of previous year.
Consequently, Joda-Time displays week 1 for 4th of January while java.time uses the US-week 2.
Solution of your problem is to specify the locale such that the formatter will use ISO-weeks so you get the same result as in Joda-Time. For example, you could choose Locale.UK which also uses English but other week rules. Don't rely on your default locale. This can fool you.
Edit: As Richard points out, I'm wrong—Java SE actually does say that the first week of a week-based year is the first Monday-based week containing at least four days, just like Joda-Time.
From the documentation of Java SE's IsoFields.WEEK_OF_WEEK_BASED_YEAR:
The week-of-week-based-year has values from 1 to 52, or 53 if the week-based-year has 53 weeks.
No mention is made of excluding any weeks, so it makes sense to assume that all weeks are counted.
From the Joda-Time Fields overview:
Weeks run from 1 to 52-53 in a week based year. The first day of the week is defined as Monday and given the value 1. The first week of a year is defined as the first week that has at least four days in the year.
January 1 and 2 comprise the first partial week of 2016, and since that's fewer than four days, Joda-Time does not count it as a week at all. January 4 is in the first week which contains four or more days.
I need the number of days in a year and I wanted to use Java8's new time api.
However, I can't do Duration.ofDays(365) because it doesn't account for leap years. And Duration.of(1, ChronoUnit.YEARS) doesn't fly because of java.time.temporal.UnsupportedTemporalTypeException: Unit must not have an estimated duration
I looked into Period, but it doesn't appear useful for going from years to days.
I feel like I'm missing something here? I could write something to add a day if the year is a leap year, but it seems like I should be able to handle this out of the box.
As per the response in Getting Duration using the new dateTime API you should be using
Period p = Period.ofYears(1);
It's important to understand the difference between Duration (exact number of nanoseconds < 1 day) and Period (variable > 1 day).
Duration won't account for leap days, daylight savings time or leap seconds, for example, and is intended for durations of less than a day, at most a few days.
So you should use Period instead.
Because different years have different number of days, if you want to find the number of days in a year, you need to specify which year you're talking about.
If you want the number of days in a specific year, you can use
Year.of(year).length()
If you want the date one year from now, you can use
LocalDate.now().plusYears(1)
or
LocalDate.now().plus(Period.ofYears(1))
If you need the number of days between two dates, you can use
ChronoUnit.DAYS.between(start, end)
So to find the number of days to the date a year from now, you can use
LocalDate today = LocalDate.now();
long days = ChronoUnit.DAYS.between(today, today.plusYears(1));
If you want to see whether a membership of one year is still valid, you can use
Period membershipLength = Period.ofYears(1);
LocalDate membershipStart = ...;
LocalDate membershipEnd = membershipStart.plus(membershipLength);
LocalDate today = LocalDate.now();
boolean memberShipEnded = today.isAfter(membershipEnd);
boolean membershipValid = !membershipEnded;
It seems clear you do not want a duration (= between two dates), but the year length of a specific date.
LocalDate dateLeap = LocalDate.of(2004, Month.MARCH, 1);
System.out.println("leap year of " + dateLeap
+ " has days: " + dateLeap.lengthOfYear());
leap year of 2004-03-01 has days: 366
Java 8 Date & Time is astonishing complete.
If you mean, in January 5th 2004 to January 5th 2005 = 366 and March 2nd 2004 to March 2rd 2005 = 365:
int lengthOfYear(LocalDate date) {
return date.getMonthValue() <= 2
? date.lengthOfYear() // Count Feb in this year
: date.plusYears(1).lengthOfYear(); // Count Feb in next year
}
Explanation: basically the length is 365. But if date is >= March, the February in the next year is counted, otherwise this year's February.
Mind that plusYears(1) will not change DAY or MONTH.
Also neither leap second nor hour/minuts on February, 29th are considered.
From http://www.coderanch.com/t/381676/java/java/number-months-between-two-given, one post mentioned:
public static int monthsBetween(Date minuend, Date subtrahend)
{
Calendar cal = Calendar.getInstance();
// default will be Gregorian in US Locales
cal.setTime(minuend);
int minuendMonth = cal.get(Calendar.MONTH);
int minuendYear = cal.get(Calendar.YEAR);
cal.setTime(subtrahend);
int subtrahendMonth = cal.get(Calendar.MONTH);
int subtrahendYear = cal.get(Calendar.YEAR);
// the following will work okay for Gregorian but will not
// work correctly in a Calendar where the number of months
// in a year is not constant
return ((minuendYear - subtrahendYear) * cal.getMaximum(Calendar.MONTH)) +
(minuendMonth - subtrahendMonth);
}
Is it true that the number of months in a Calendar is not constant? And why?
Yes. In the hebrew calendar, there are several years with 13 months (7 out of 19 to be exact).
It is funny that, month comes from moon. A lunar calendar usually sync days with moon phases, so that, for example, day 15 of any month is always a full moon day.
The problem is a solar year is not exactly 12 moon cycles. So a lunar calendar must have "leap months".