Reputation: 480
I need to get week span: start of week (Mo) | end of week (Sun) in days of year.
For example, for 5.9.2019, it should be 241-247 (days starting from 0).
However, my method produces 250 | 249
public static Pair<Integer, Integer> getWeekSpan(Timestamp date) {
Pair<Integer, Integer> result = new Pair<Integer, Integer>();
Calendar cal = timestampToCalendar(date);
// start of week
cal.set(Calendar.DAY_OF_WEEK, cal.getActualMinimum(Calendar.DAY_OF_WEEK));
result.setKey(cal.get(Calendar.DAY_OF_YEAR) - 1);
// end of week
cal.set(Calendar.DAY_OF_WEEK, cal.getActualMaximum(Calendar.DAY_OF_WEEK));
result.setValue(cal.get(Calendar.DAY_OF_YEAR) - 1);
return result;
}
public static Calendar timestampToCalendar(Timestamp timestamp) {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(timestamp.getTime());
return cal;
}
Upvotes: 3
Views: 268
Reputation: 480
Setting straignt Monday and Sunday solved the problem.
public static Pair<Integer, Integer> getWeekSpan(Timestamp date) {
Pair<Integer, Integer> result = new Pair<Integer, Integer>();
Calendar cal = timestampToCalendar(date);
// start of week
cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
result.setKey(cal.get(Calendar.DAY_OF_YEAR) - 1);
// end of week
cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
result.setValue(cal.get(Calendar.DAY_OF_YEAR) - 1);
return result;
}
EDIT: However this piece of code does solve the initial problem, it does not solve time zone problem and uses deprecated classes. See other answers for more detail.
Upvotes: 0
Reputation: 340070
If you must start with a java.sql.Timestamp
because of interoperating with old code not yet updated to java.time classes, you can convert back and forth.
Instant
You will find new to…
/from…
conversion methods added to the old classes. The java.sql.Timestamp
class offers the toInstant
method to render a java.time.Instant
object. Both the old and new classes represent a moment in UTC with a resolution of nanoseconds.
Instant instant = myJavaUtilTimestamp.toInstant() ;
The next problem is that we have a moment in UTC, a point on the timeline. But your business problem requires a date on the calendar. The trick is that for any given moment the date varies around the globe by time zone. For example, a few minutes after midnight in Paris France is a new day while still “yesterday” in Montréal Québec.
ZonedDateTime
So you must specify the time zone by which you want determine a date. Apply a ZoneId
to get a ZonedDateTime
object.
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
LocalDate
The zdt
represents the very same simultaneous moment as the instant
. The difference is that the zdt
is viewed through the offset used by the people of that time zone’s region. We can therefore extract the date (year-month-day) from that ZonedDateTime
as seen by the people of Québec at that moment, given this example. The date-only value is represented by the LocalDate
class, having no time-of-day and no time zone.
LocalDate localDate = zdt.toLocalDate() ;
Now, with this localDate
object in hand, you may follow the instructions in this Answer by Ole V.V.
Upvotes: 1
Reputation: 86379
public static Pair<Integer, Integer> getWeekSpan(LocalDate date) {
return new Pair<>(getZeroBasedDayOfYear(date.with(DayOfWeek.MONDAY)),
getZeroBasedDayOfYear(date.with(DayOfWeek.SUNDAY)));
}
private static int getZeroBasedDayOfYear(LocalDate date) {
return date.getDayOfYear() - 1;
}
Output:
[244, 250]
It doesn’t agree exactly with what you said you expected, but it does agree with what I get from running the code in your own answer, so I trust that it is correct. The numbers correspond to Monday, September 2 and Sunday, September 8, 2019.
I don’t know whether your Pair
class has got a 2-argument constructor. If not, I trust you to fill in the two integers in the same way you did in the question.
Beware that a week may cross New Year. If for instance I specify 2019-12-31, I get:
[363, 4]
4 refers to January 5 of the following year.
My code relies on the fact that you want ISO weeks, where Monday is the first day of the week. If required, we can introduce more flexibility through a WeekFields
object:
public static Pair<Integer, Integer> getWeekSpan(LocalDate date) {
WeekFields wf = WeekFields.ISO;
return new Pair<>(getZeroBasedDayOfYear(date.with(wf.dayOfWeek(), 1)),
getZeroBasedDayOfYear(date.with(wf.dayOfWeek(), 7)));
}
As long as I use WeekFields.ISO
, the result is still the same, but this is where you might plug in, for example WeekFields.of(Locale.getDefault())
if you wanted to use the week definition of the default locale.
The classes you were using, Timestamp
and Calendar
, are poorly designed and long outdated. I recommend you don’t use them. Instead I am using LocalDate
from java.time, the modern Java date and time API.
If you’ve got a Timestamp
object from a legacy API that you cannot change or don’t want to upgrade just now, the way to convert to a LocalDate
is:
LocalDate convertedLocalDate = yourTimestamp.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDate();
This is one of the places where the Calendar
class is getting confusing. It numbers the days of the week from 1 for Sunday through 7 for Saturday. So when you ask for the actual minimum and maximum, you get 1 for Sunday and 7 for Saturday even though these are not the first and the last day of the week. So what happened was that you first set the day of week to Sunday, yielding September 8 because your week starts on Monday. BTW, Calendar.getInstance()
picks up the week definition from your default locale, which cannot be read from the code and is likely to confuse many too. And even more so if one day your code runs on a JVM with a different default locale and suddenly behaves differently. Next when setting the day of the week to max, 7, you got Saturday, September 7. The dates were translated correctly to 250 and 249.
Oracle tutorial: Date Time explaining how to use java.time.
Upvotes: 4