I want to get Monday date from given week and year using Java 8 package java.time.
But at some point I am facing issue as it's not returning proper date.
private LocalDate getDateFromWeekAndYear(final String week,final String year){
LocalDate date = LocalDate.now();
date = date.with(WeekFields.ISO.dayOfWeek(), 1);
date = date.with(WeekFields.ISO.weekOfWeekBasedYear(), Long.parseLong(week));
date = date.with(WeekFields.ISO.weekBasedYear(), Long.parseLong(year));
return date;
}
For example:
If I pass week=1 and year=2013 then date is : 2012-12-31.
But if I pass week=53 and year=2015 then date is : 2014-12-29. I expected 2014-12-28.
Is there any logical mistake I am making or some other issue ?
This is astonishingly more difficult than the partially invalid expectations of OP and most answers show.
First to say: It is very important to define the correct order of week-based-manipulations. The OP has first applied day-manipulation, then year-based manipulation. The correct approach is in reverse! I will show the right helper method implementation:
public static void main(String... args) {
System.out.println(
getDateFromWeekAndYear("53", "2015")); // 2015-12-28, NOT 2014-12-28
System.out.println(
getDateFromWeekAndYear("53", "2015").get(WeekFields.ISO.weekOfWeekBasedYear())); // 53
System.out.println(
getDateFromWeekAndYear("53", "2014")); // 2014-12-29
System.out.println(
getDateFromWeekAndYear("53", "2014").get(WeekFields.ISO.weekOfWeekBasedYear())); // 1
}
private static LocalDate getDateFromWeekAndYear(final String week,final String year) {
int y = Integer.parseInt(year);
LocalDate date = LocalDate.of(y, 7, 1); // safer than choosing current date
// date = date.with(WeekFields.ISO.weekBasedYear(), y); // no longer necessary
date = date.with(WeekFields.ISO.weekOfWeekBasedYear(), Long.parseLong(week));
date = date.with(WeekFields.ISO.dayOfWeek(), 1);
return date;
}
If you don't respect this specific order then you will indeed get sometimes a 2014-date for the input 2015-W53 (depending on the current date).
Second problem: I have also avoided to start with current date in order to be not near start or end of calendar year (calendar year != week-based-year) and instead chosen midth of year as starting point.
The third problem is lenient handling of week 53 in (week-based)-year 2014. It does not exist because 2014 had only 52 weeks!!! A strict algorithm should recognize and reject such an input. Therefore I advise against using YearWeek.of(2014, 53) (in the external library Threeten-Extra) resulting in the first week of 2015, see also its javadoc. Better than such lenient handling would have been
YearWeek yw = YearWeek.of(2014, 52);
if (yw.is53WeekYear()) {
yw = YearWeek.of(2014, 53);
}
or using this code from my own time library Time4J (whose class CalendarWeek has extra i18n-features and extra week arithmetic in comparison with YearWeek):
CalendarWeek.of(2014, 53); // throws an exception
System.out.println(CalendarWeek.of(2014, 1).withLastWeekOfYear()); // 2014-W52
Only using java.time-package:
Using such external libraries would at least have helped to solve the first problem in a transparent way. If you are not willing to add an extra dependency then you can do this to handle week 53 if invalid:
If the expression WeekFields.ISO.weekOfWeekBasedYear() applied on the result of your helper method yields the value 1 then you know that week 53 was invalid. And then you can decide if you want to accept lenient handling or to throw an exception. But silent adjusting such an invalid input is IMHO bad design.
First, you need to calculate the first Monday of the first week of the year,
then simply plus multi of 7 to the date.
public static LocalDate firstMonday(int week, int year) {
LocalDate firstMonOfFirstWeek = LocalDate.now()
.with(IsoFields.WEEK_BASED_YEAR, year) // year
.with(IsoFields.WEEK_OF_WEEK_BASED_YEAR, 1) // First week of the year
.with(ChronoField.DAY_OF_WEEK, 1); // Monday
// Plus multi of 7
return firstMonOfFirstWeek.plusDays( (week - 1) * 7);
}
public static void main(String[] args) {
System.out.println(firstMonday(1, 2013)); // 2012-12-31
System.out.println(firstMonday(53 ,2015 )); // 2015-12-28
}
tl;dr
YearWeek.of ( 2013 , 1 ).atDay ( DayOfWeek.MONDAY )
Incorrect expectations
Your expectations are not correct. The Monday of 2015-W53 is 2015-12-28, not 2014-12-28, year 2015 rather than 2014. There is no reason to expect 2014. Please edit your Question to explain your thoughts, if you need more explanation.
You may be confused about a calendar-year versus a week-based year. In the ISO 8601 definition of a week based year, week number one contains the first Thursday of the calendar-year. This means we have some overlap between the years. The last few days of a calendar-year may reside in the following week-based-year. And vice-versa, the first few days of a calendar-year may reside in the previous week-based-year.
As an example, you can see in the screenshot below, the last day of calendar 2012 (December 31) falls in week one of the following week-based-year of 2013 at week number 1. And in the other screen shot, we have the opposite, where the first three days of calendar 2016 (January 1, 2, & 3) land in the week-based-year of 2015 at week number 53.
The Monday of 2013-W01 is 2012-12-31.
The Monday of 2015-W53 is 2015-12-28.
YearWeek
I suggest adding the ThreeTen-Extra library to your project to make use of of the YearWeek class. Rather than pass around mere integer numbers for year and week, pass around objects of this class. Doing so makes your code more self-documenting, provides type-safety, and ensures valid values.
// Pass ( week-based-year-number, week-number ). *Not* calendar year! See the ISO 8601 standard.
YearWeek yw = YearWeek.of( 2013 , 1 );
You can pull any day from that week.
LocalDate ld = yw.atDay( DayOfWeek.MONDAY );
Let's try this kind of code.
YearWeek yw1 = YearWeek.of ( 2013 , 1 );
LocalDate ld1 = yw1.atDay ( DayOfWeek.MONDAY );
YearWeek yw2 = YearWeek.of ( 2015 , 53 );
LocalDate ld2 = yw2.atDay ( DayOfWeek.MONDAY );
System.out.println ( "yw1: " + yw1 + " Monday: " + ld1 );
System.out.println ( "yw2: " + yw2 + " Monday: " + ld2 );
yw1: 2013-W01 Monday: 2012-12-31
yw2: 2015-W53 Monday: 2015-12-28
Tip: To see those ISO 8601 standard week numbers on a Mac in Calendar.app, set System Preferences > Language & Region > Calendar > ISO 8601. Then in Calendar.app, set Preferences > Advanced > Show week numbers.
I wanted Monday of a week about 6 month ago - to be specific 26 weeks ago.. below code gave me the required date:
LocalDate.now().minusWeeks(26).with(WeekFields.ISO.dayOfWeek(), DayOfWeek.MONDAY.getValue())
Related
I am working on a project. There I should find the total weeks of a year. I tried with the following code, but I get the wrong answer: 2020 has 53 weeks, but this code gives 52 weeks.
Where have I gone wrong in this code?
package com.hib.mapping;
import java.time.LocalDate;
import java.time.temporal.WeekFields;
import java.util.Calendar;
import java.util.GregorianCalendar;
import org.joda.time.DateTime;
public class TestWeek {
public static void main(String args[]) {
System.out.println(getWeeks());
}
public static int getWeeks() {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, 2020);
cal.set(Calendar.MONTH, Calendar.JANUARY);
cal.set(Calendar.DAY_OF_MONTH, 1);
GregorianCalendar gregorianCalendar = new GregorianCalendar();
int weekDay = cal.get(Calendar.DAY_OF_WEEK) - 1;
if (gregorianCalendar.isLeapYear(2020)) {
if (weekDay == Calendar.THURSDAY || weekDay == Calendar.WEDNESDAY)
return 53;
else
return 52;
} else {
if (weekDay == Calendar.THURSDAY)
return 53;
else
return 52;
}
}
}
Output:
52
tl;dr
For a standard ISO 8601 week, use the YearWeek class from ThreeTen-Extra library with a ternary statement.
YearWeek // Represents an entire week of a week-based-year.
.of( 2020 , 1 ) // Pass the number of the week-based-year (*not* calendar year), and a week number ranging from 1 to 52 or 1 to 53.
.is53WeekYear() // Every standard week-based-year has either 52 or 52 complete weeks.
? 53 // Ternary statement returns 53 if the predicate returns True, …
: 52 // … otherwise returns 52.
That is, YearWeek.of( 2020 , 1 ).is53WeekYear() ? 53 : 52
Define “week”
You need to define a week. In your code sample, the definition of week varies by the JVM’s current default Locale. So your results may vary at runtime.
Your code also uses terrible date-time classes that were supplanted years ago by the modern java.time classes. Stop using GregorianCalendar & Calendar; they were replaced for good reasons.
ISO 8601 week
The ISO 8601 standard defines a week as:
Weeks start on Monday, end on Sunday.
Week # 1 has the first Thursday of the calendar-year.
That definition means:
The first and last few days of a week-based-year may be the trailing/leading days of the previous/following calendar-year.
The week-based-year has either 52 or 53 complete weeks.
If your definition differs, see the Answer by Ole V.V..
YearWeek:is53WeekYear
If this matches your definition, then add the ThreeTen-Extra library to your project to extend the java.time functionality built into Java 8 and later. You then have access to the YearWeek class.
ZoneId z = ZoneId.of( "America/Montreal" ) ;
YearWeek yearWeekNow = YearWeek.now( z ) ;
boolean is53WeekYear = yearWeekNow.is53WeekYear() ;
int weeksLong = yearWeekNow.is53WeekYear() ? 53 : 52 ;
To ask about a particular week-based-year, just arbitrarily pick any week of the year. For example, for the week-based year 2020 we ask for week # 1.
int weeksLong = YearWeek.of( 2020 , 1 ).is53WeekYear() ? 53 : 52 ;
LocalDate weekStart = YearWeek.of( 2020 , 1 ).atDay( DayOfWeek.MONDAY ) ;
weeksLong = 53
weekStart = 2019-12-30
Notice how the first day of the week-based-year of 2020 is from the calendar-year 2019.
Using the Wikipedia definition here. A year has 53 weeks if 1st Jan is a Thursday, or 31st Dec is a Thursday, otherwise it has 52 weeks. This definition is equivalent to the one you used. I think this is a way easier condition to check for, as you don't need to check for leap years.
Using the Java 8 java.time APIs:
int year = 2020;
boolean is53weekYear = LocalDate.of(year, 1, 1).getDayOfWeek() == DayOfWeek.THURSDAY ||
LocalDate.of(year, 12, 31).getDayOfWeek() == DayOfWeek.THURSDAY;
int weekCount = is53weekYear ? 53 : 52;
The flexible solution
This should work for any week numbering scheme that can be represented in a WeekFields object.
public static int noOfWeeks(WeekFields wf, int year) {
LocalDate lastDayOfYear = YearMonth.of(year, Month.DECEMBER).atEndOfMonth();
if (lastDayOfYear.get(wf.weekBasedYear()) > year) { // belongs to following week year
return lastDayOfYear.minusWeeks(1).get(wf.weekOfWeekBasedYear());
}
else {
return lastDayOfYear.get(wf.weekOfWeekBasedYear());
}
}
The idea is to find the week number of the last week of the week based year. I try first with 31 December, but that may be in the first week of the following year. If so, I go one week back.
I have tested pretty thoroughly with WeekFields.ISO, not so much with other WeekFields objects, but as I said, I believe it works.
If you know for a fact that you will always need ISO 8601 weeks, I think you should go with one of the good answers by Sweeper and by Basil Bourque. I posted this in case you needed a more flexible solution that would work with other week numbering schemes too.
Use java.time
The code in your question is funny in that it imports classes both from Joda-Time and from java.time, yet uses the old Calendar and GregorianCalendar from Java 1.1. These classes were poorly designed and are now long outdated, you should not use them. Joda-Time is in maintenance mode, java.time has taken over after it. Which is what I use and recommend that you use.
I think this should work just fine as well:
int year = 2020;
long numOfWeeks = LocalDate.of(year, 1, 1).datesUntil(LocalDate.of(year, 12, 31), Period.ofDays(7)).count();
System.out.println("Weeks: " + numOfWeeks);
Below code works for me.
public static int getTotalWeeksInYear(int year){
int totalWeeks=0;
Calendar calendar=Calendar.getInstance();
for(int month=0;month<12;mmonth++){
int day=1;
do{
calendar.set(year, month, day);
if(calendar.get(Calendar.DAY_OF_WEEK)==5)
totalWeeks++;
day++;
}while (day <=calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
}
return totalWeeks;
}
Using Java.time
public static long getTotalWeekByLocalDate(LocalDate ldate) {
long weeksInYear = IsoFields.WEEK_OF_WEEK_BASED_YEAR.rangeRefinedBy(ldate).getMaximum();
return weeksInYear;
}
After trying a lot in java 8. I could not find a solution. then I prepared Joda date and time dependency. It gave me a good answer as I expected
code:
for (int i = 2020; i < 2100; i++) {
int weeks = new DateTime().withYear(i).weekOfWeekyear().getMaximumValue();
System.out.println(i + " years : " + weeks);
}
Maven Dependency:
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.5</version>
</dependency>
For a span of time running from one date to another date, how to get the number of calendar months containing one or more days of my span?
So for example:
2016-01-23/2016-01-23 = 1 calendar month (January)
2016-01-31/2016-02-01 = 2 calendar months (January, February)
2016-01-23/2016-02-28 = 2 calendar months (January, February)
2016-01-15/2016-03-15 = 3 calendar months (January, February, March)
2016-01-15/2017-03-15 = 15 calendar months (Jan-Dec of 2016 plus January, February, March of 2017)
I do not define a month as “30 days”. I am asking about calendar months, January-December.
Similar to this Question but that asks about PHP/MySQL.
Calculate the "epoch" month of both dates, then subtract them and add 1.
Using LocalDate like in the other answer, an epochMonth() helper method makes it easy:
private static int monthsTouched(LocalDate fromDate, LocalDate toDate) {
return epochMonth(toDate) - epochMonth(fromDate) + 1;
}
private static int epochMonth(LocalDate date) {
return date.getYear() * 12 + date.getMonthValue();
}
Like the results in the question, both dates are inclusive.
Note: Validation skipped for brevity, e.g. what is result if fromDate > toDate?
Test
public static void main(String[] args) {
test("2016-01-23", "2016-01-23");
test("2016-01-31", "2016-02-01");
test("2016-01-23", "2016-02-28");
test("2016-01-15", "2016-03-15");
test("2016-01-15", "2017-03-15");
}
private static void test(String fromDate, String toDate) {
System.out.println(monthsTouched(LocalDate.parse(fromDate), LocalDate.parse(toDate)));
}
Output (matches results from question)
1
2
2
3
15
Use ChronoField.PROLEPTIC_MONTH, which returns a count of months from year zero:
import static java.time.temporal.ChronoField.PROLEPTIC_MONTH;
long monthsTouched = date2.getLong(PROLEPTIC_MONTH) - date1.getLong(PROLEPTIC_MONTH) + 1;
Adjust the start and end dates
The key is to adjust your dates.
Move the starting date to the first of the month
Move the ending date to the first of the following month
We move the ending to the next month after because the Half-Open approach is commonly used when considering spans of time. Half-Open means the beginning is inclusive while the ending is exclusive. So lunch hour runs from 12:00 to 13:00 but does not include the 61st minute of 1 PM. A week runs from Monday to Monday, for seven days not including that second Monday.
So a span running from the first of January to the first of March is two months rather than three because we run up to, but do not include, that last date, the first of March.
java.time
The java.time classes built into Java 8 and later make easier work of this.
For date-only values, without a time-of-day and without a time zone, use the LocalDate class.
LocalDate start = LocalDate.parse( "2016-01-31" );
LocalDate stop = LocalDate.parse( "2016-02-01" );
To adjust, use a TemporalAdjuster. Implementations can be found in the TemporalAdjusters class (note the plural 's'). We need firstDayOfMonth and firstDayOfNextMonth.
LocalDate startAdjusted = start.with( TemporalAdjusters.firstDayOfMonth() );
LocalDate stopAdjusted = stop.with( TemporalAdjusters.firstDayOfNextMonth() );
Now use the ChronoUnit class to calculate elapsed whole months.
long calendarMonthsTouched = ChronoUnit.MONTHS.between( startAdjusted , stopAdjusted );
span: 2016-01-31/2016-02-01
calendarMonthsTouched: 2
See this code live in IdeOne.com.
I'm using $year and $week in MongoDB aggregation query to group results by year and week of year. In the Java code I want to convert the returned year, week of year to a DateTime object, to allow easier presentation of the data.
It seems that Joda DateTime's getWeekOfWeekyear() doesn't behave the same way as $week in MongoDB, and this causes different date results.
Scenario 1
MongoDB query:
db.test.aggregate(
{$project:
{week: {$week: ISODate("2016-01-01T00:00:00.000Z") },
year: {$year: ISODate("2016-01-01T00:00:00.000Z") } }
}
)
Returns:
{ "_id" : "", "week" : 0, "year" : 2016 }
When trying to convert those values to Joda DateTime object, it throws an exception: IllegalFieldValueException.
(new DateTime(0, DateTimeZone.UTC)).withWeekyear(2016).withWeekOfWeekyear(0).withDayOfWeek(1).toString()
Scenario 2
In addition, when querying for 2015-05-10, which is Sunday.
MongoDB query:
db.test.aggregate(
{$project:
{week: {$week: ISODate("2016-01-01T00:00:00.000Z") },
year: {$year: ISODate("2016-01-01T00:00:00.000Z") } }
}
)
Returns:
{ "_id" : "", "week" : 19, "year" : 2015 }
But when trying to convert to Joda DateTime, this results in the previous week, starts at 2015-05-04:
(new DateTime(0, DateTimeZone.UTC)).withWeekyear(2015).withWeekOfWeekyear(19).withDayOfWeek(1).toString()
results in:
2015-05-04T00:00:00.000Z
Mongo $week operator returns the week of the year as a number between 0 and 53. Weeks begin on Sundays, and week 1 begins with the first Sunday of the year. Days preceding the first Sunday of the year are in week 0. In Java, WeekOfYear returned value, the first week of the year is that in which at least 4 days are in the year. As a result of this definition, day 1 of the first week may be in the previous year. Also week starts on Monday.
Is there a way to solve this inconsistency in the Java code?
ISO 8601
Joda-Time follows the ISO 8601 standard in defining weeks.
Monday is the first day of the week.
Weeks are numbered 1 to 52 or 53.
Week numbers are written with an uppercase W, such as W23.Year may be prepended, 2015-W23.
Week # 1, W01, contains the year's first Thursday.
As far as I know, this standard definition has been growing more common in usage in various countries and industries.
Sunday Weeks
The MongoDB doc defines weeks as:
…the week of the year for a date as a number between 0 and 53.
Weeks begin on Sundays, and week 1 begins with the first Sunday of the year. Days preceding the first Sunday of the year are in week 0. This behavior is the same as the “%U” operator to the strftime standard library function.
As far as I know, this is a mostly American definition, not used much outside the US.
Why does that definition say 0 to 53? That means "up to 54 weeks". I don't think this definition would produce 54 weeks in any year, but I've not thought it through.
Why Mix?
You cannot really mix the two definitions. Why bother? If your goal is to use MongoDB’s definition of weeks, and represent them by a date-time, then write your own converter.
My own advice would be to ditch MongoDB’s definition and function, and stick with the standard definition.
Find Sunday
If you want to find the Sunday starting a week in MongoDB’s world, write your own little function. Feed in the year number and week number, and get back a DateTime. In this scenario, you have no need for Joda-Time’s week-of-year features.
Something like this.
int yearNumber = 2015;
int weekNumber = 0;
LocalDate firstWeekSunday = null;
LocalDate firstOfYear = new LocalDate ( yearNumber, 1, 1 );
if ( firstOfYear.getDayOfWeek ( ) == DateTimeConstants.SUNDAY ) {
firstWeekSunday = firstOfYear;
} else { // ELSE not Sunday.
firstWeekSunday = firstOfYear.minusDays ( firstOfYear.getDayOfWeek ( ) ); // Joda-Time uses standard ISO 8601 weeks, where Monday = 1, Sunday = 7.
}
LocalDate sunday = firstWeekSunday.plusWeeks ( weekNumber );
DateTimeZone zone = DateTimeZone.forID ( "America/Montreal" );
DateTime dateTime = sunday.toDateTimeAtStartOfDay ( zone );
Dump to console.
System.out.println ( "Sunday-based week of year:" + yearNumber + " week: " + weekNumber + " starts: " + sunday + "." );
System.out.println ( "Adjusted to time zone: " + zone + " is: " + dateTime + "." );
When run.
Sunday-based week of year:2015 week: 0 starts: 2014-12-28.
Adjusted to time zone: America/Montreal is: 2014-12-28T00:00:00.000-05:00.
I have a javafx.scene.control.DatePicker. I want to extract the (Locale) week number from the selected date. Until now i haven't found a solution and i prefer not to write my own algorithm. I use Java8 and hope it is possible in the new java time library.
The Java-8-solution can take into account the local definition of a week using the value of a date-picker:
LocalDate date = datePicker.getValue(); // input from your date picker
Locale locale = Locale.US;
int weekOfYear = date.get(WeekFields.of(locale).weekOfWeekBasedYear());
Also keep in mind that the popular alternative Joda-Time does not support such a localized week-of-year fields. For example: In ISO-8601 (widely used) the week starts with Monday, in US with Sunday. Also the item when the first week of year starts in a given calendar year is dependent on the locale.
You can use http://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html#get-java.time.temporal.TemporalField-
LocalDate localDate = LocalDate.of(2014, 9, 18); // assuming we picked 18 September 2014
int weekNumber = localDate.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR);
This will give you the week number based on ISO convention.
For a locale based evaluation :
LocalDate localDate = LocalDate.of(2014, 9, 18); // assuming we picked 18 September 2014
WeekFields weekFields = WeekFields.of(Locale.US);
int weekNumber = localDate.get(weekFields.weekOfWeekBasedYear());
FX DatePicker is based on the new (to jdk8) Date/Time api - time to learn how-to use it (not entirely sure I found the shortest way, though - corrections welcome :-)
The picker's value is a LocalDate, which can be queried for certain TemporalFields. Locale-aware week-related fields are provided by the WeekFields class, f.i. weekOfYear:
DatePicker picker = new DatePicker();
picker.valueProperty().addListener((p, oldValue, newValue) -> {
if (newValue == null) return;
WeekFields fields = WeekFields.of(Locale.getDefault());
// # may range from 0 ... 54 without overlapping the boundaries of calendar year
int week = newValue.get(fields.weekOfYear());
// # may range from 1 ... 53 with overlapping
int weekBased = newValue.get(fields.weekOfWeekBasedYear());
LOG.info("week/Based " + week + "/" + weekBased);
});
To see the difference, choose f.i. January 2012 (in locales that start a week at Monday). Which one to actually use, depends on context - the picker itself uses weekOfYear (if showWeekNumbers is enabled)
You can also use the DateTimeFormatter, looks easier for me :
LocalDate date = LocalDate.now();
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("w");
int week = Integer.parseInt(date.format(dtf));
How do you determine which day of the week is considered the “start” according to a given Locale using Joda-Time?
Point: Most countries use the international standard Monday as first day of week (!). A bunch others use Sunday (notably USA). Others apparently Saturday. Some apparently Wednesday?!
Wikipedia "Seven-day week"#Week_number
Joda-Time uses the ISO standard Monday to Sunday week.
It does not have the ability to obtain the first day of week, nor to return the day of week index based on any day other than the standard Monday. Finally, weeks are always calculated wrt ISO rules.
There's no reason you can't make use of the JDK at least to find the "customary start of the week" for the given Locale. The only tricky part is translating constants for weekdays, where both are 1 through 7, but java.util.Calendar is shifted by one, with Calendar.MONDAY = 2 vs. DateTimeConstants.MONDAY = 1.
Anyway, use this function:
/**
* Gets the first day of the week, in the default locale.
*
* #return a value in the range of {#link DateTimeConstants#MONDAY} to
* {#link DateTimeConstants#SUNDAY}.
*/
private static final int getFirstDayOfWeek() {
return ((Calendar.getInstance().getFirstDayOfWeek() + 5) % 7) + 1;
}
Add a Locale to Calendar.getInstance() to get a result for some Locale other than the default.
Here is how one might work around Joda time to get the U.S. first day of the week:
DateTime getFirstDayOfWeek(DateTime other) {
if(other.dayOfWeek.get == 7)
return other;
else
return other.minusWeeks(1).withDayOfWeek(7);
}
Or in Scala
def getFirstDayOfWeek(other: DateTime) = other.dayOfWeek.get match {
case 7 => other
case _ => other.minusWeeks(1).withDayOfWeek(7)
}
Seems like you're out of luck, it looks like all of the provided Chronologies inherit the implementation from baseChronology, which supports only ISO definitions,
i.e. Monday=1 ... Sunday=7.
You would have to define your own LocaleChronology, possibly modeled on StrictChronology or LenientChronology, add a factory method:
public static LocaleChronology getInstance(Chronology base, Locale locale)
and override the implementation of
public final DateTimeField dayOfWeek()
with a re-implementation of java.util.Calendar.setWeekCountData(Locale desiredLocale) which relies on sun.util.resources.LocaleData..getCalendarData(desiredLocale).
This is what I came up with. The startOfWeek will always be the start of a Sunday and the endOfweek will always be an end of a Saturday(Start a of Monday).
DateTime startOfWeek;
DateTime endOfWeek;
// make sure Sunday is the first day of the week, not Monday
if (dateTime.getDayOfWeek() == 7) {
startOfWeek = dateTime.plusDays(1).weekOfWeekyear().roundFloorCopy().minusDays(1);
endOfWeek = dateTime.plusDays(1).weekOfWeekyear().roundCeilingCopy().minusDays(1);
} else {
startOfWeek = dateTime.weekOfWeekyear().roundFloorCopy().minusDays(1);
endOfWeek = dateTime.weekOfWeekyear().roundCeilingCopy().minusDays(1);
}
Here is the Scala code to get start and end day of week dynamically.
Note:- Make sure start and end are in order for example when start day is Sunday then Monday is end day of week
def shiftWeeksBy(startDayOfWeek: Int, currentWeekDay: Int) : Int = (startDayOfWeek, currentWeekDay) match {
case (s, c) if s <= c | s == 1 => 0 //start day of week is Monday -OR- start day of week <= current week day
case _ => 1
}
def getStartAndEndDays(initialTime: DateTime, startDayOfWeek: Int, endDayOfWeek: Int): (Option[DateTime], Option[DateTime]) = {
val currentDateWeekDay = initialTime.dayOfWeek.get
(Some(initialTime.minusWeeks(shiftWeeksBy(startDayOfWeek, currentDateWeekDay)).withDayOfWeek(startDayOfWeek).withTimeAtStartOfDay()),
Some(initialTime.plusWeeks(shiftWeeksBy(currentDateWeekDay, endDayOfWeek)).withDayOfWeek(endDayOfWeek)))
}
Output:- For 5th Jan 2021 start day of week is Thursday and end day of week is Wednesday then week begins with 2020-12-31 and end with 2021-01-06.
scala> getStartAndEndDays(new DateTime("2021-01-05T00:00:00.000"), 4, 3)
res5: (Option[org.joda.time.DateTime], Option[org.joda.time.DateTime]) = (Some(2020-12-31T00:00:00.000+05:30),Some(2021-01-06T00:00:00.000+05:30))
So your question is, how to get the DayOfWeek from a Joda DateTime object? What about this:
DateTime dt = new DateTime().withYear(2009).plusDays(111);
dt.toGregorianCalendar().getFirstDayOfWeek();
I used the following stub in Scala to obtain first and last days of the week from Joda DateTime
val today: DateTime = new DateTime()
val dayOfWeek: DateTime.Property = today.dayOfWeek()
val firstDayOfWeek: DateTime = dayOfWeek.withMinimumValue().minusDays(1)
val lastDayOfWeek: DateTime = dayOfWeek.withMaximumValue().minusDays(1)
Note: The minusDays(1) is only meant to make the week span from Sunday to Saturday (instead of the default Monday to Sunday known to Joda). For US (and other similar) locales, you can ignore this part