I would like to have a class to represent a particular week of year - for example today it's 29 November, which is exactly week number 48 of year 2014. So, one example implementation would be:
import java.time.Year;
public class WorkingWeek {
private int weekNumber;
private Year year;
}
Another conception is to have just a week's Monday date in a WorkingWeek class, but I feel it's less intuitive and I will frequently use week-in-year number.
Is there a class in Java 8 Time API that would fit best my requirements? Or if not, what would be recommended approach?
You need to specify your definition of a week.
String Representation Of A Year-Week
One option is strings. The ISO 8601 standard defines a week as beginning on a Monday, ending on Sunday, with the first week of the year being the first to contain a Thursday, resulting in 52 or 53 weeks a year.
The standard also defines a string representation for this week-of-year span of time in the format of YYYY-Www (or omitting hypen, YYYYWww) such as 2014-W07. A day within the week is represented by a digit where Monday is 1 and Sunday is 7, in the format YYYY-Www-D (or omitting hyphen, YYYYWwwD) such as 2014-W07-2 (a Tuesday in 7th week of year). The W is important to disambiguate from a year-month such as 2014-07 being July of 2014.
java.time
The java.time package built into Java 8 and later is inspired by Joda-Time but entirely re-architected. See Tutorial.
In java.time, an Instant is a moment on the timeline in UTC. Apply a time zone (ZoneId) to get a ZonedDateTime. Use LocalDate to get a date-only value with no time-of-day and no time zone.
Note that determining the first moment of a day in java.time requires an extra step when compared to Joda-Time: We must go through the LocalDate class to call its atStartOfDay method.
ZoneId zoneId = ZoneId.of ( "America/Montreal" );
ZonedDateTime now = ZonedDateTime.now ( zoneId );
LocalDate firstDayOfThisWeek = now.toLocalDate ().with ( DayOfWeek.MONDAY );
LocalDate firstDayOfNextWeek = firstDayOfThisWeek.plusWeeks ( 1 );
ZonedDateTime thisWeekStart = firstDayOfThisWeek.atStartOfDay ( zoneId );
ZonedDateTime nextWeekStart = firstDayOfNextWeek.atStartOfDay ( zoneId );
Unfortunately, java.time lacks the equivalent of Joda-Time's Interval.
Fortunately we have ThreeTen Extra, the project that extends java.time (310 being the number of the JSR defining java.time). This library includes an Interval class that integrates with java.time. This Interval class is more limited than that of Joda-Time as it supports only Instant objects without time zones (always in UTC).
Caution: The ThreeTen-Extra project reserves the right to change its interfaces and/or implementations. While intended to be useful as-is, it also serves as an experimental proving ground for classes that may be eventually incorporated into java.time. I gladly make use of ThreeTen-Extra, but you must make your own risk-benefit decision.
// This next line requires adding the `ThreeTen Extra` library to your project.
Interval interval = Interval.of ( thisWeekStart.toInstant () , nextWeekStart.toInstant () ); // "Interval" is part of ThreeTen-Extra project, not built into Java 8.
Dump to console.
System.out.println ( "now: " + now + " thisWeekStart: " + thisWeekStart + " nextWeekStart: " + nextWeekStart + " interval: " + interval );
now: 2016-01-15T18:10:48.143-05:00[America/Montreal] thisWeekStart: 2016-01-11T00:00-05:00[America/Montreal] nextWeekStart: 2016-01-18T00:00-05:00[America/Montreal] interval: 2016-01-11T05:00:00Z/2016-01-18T05:00:00Z
You can determine the week-of-year as defined by the ISO 8601 standard. Note the "week based" terms. Near the beginning or ending of the year, a date will be in one calendar year while its ISO 8601 week’s year may be ±1.
ZoneId zoneId = ZoneId.of ( "America/Montreal" );
ZonedDateTime now = ZonedDateTime.now ( zoneId );
int weekOfYear = now.get ( IsoFields.WEEK_OF_WEEK_BASED_YEAR );
int weekBasedYear = now.get ( IsoFields.WEEK_BASED_YEAR );
System.out.println ( "weekOfYear: " + weekOfYear + " of weekBasedYear: " + weekBasedYear );
weekOfYear: 2 of weekBasedYear: 2016
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
Joda-Time
UPDATE: The Joda-Time project is now in maintenance mode and advises migration to the java.time classes. I am leaving this section intact as history.
The Joda-Time 2.5 library offers the Interval class to represent a span of time as a pair of specific moments in time along the timeline of the Universe. Each moment is represented by the DateTime class.
Half-Open
Joda-Time uses the Half-Open [) approach to defining spans of time. The beginning is inclusive while the ending is exclusive. This is generally the best way to work with such spans of time. Search StackOverflow for many examples and discussions.
ISO 8601 in Joda-Time
Joda-Time uses the ISO 8601 definition of weeks. Also, Joda-Time uses ISO 8601 as its defaults for parsing and generating string representations of date-time values.
DateTimeZone zone = DateTimeZone( "America/Montreal" );
// Get first moment of a Monday. Inclusive.
DateTime start = new DateTime( 2014, 11, 24, 0, 0, 0, zone ); // Handle exception thrown if occurring during a Daylight Saving Time gap.
DateTime stop = start.plusWeeks( 1 ); // First moment of following Monday. Exclusive.
Interval week = new Interval ( start, stop );
First Monday
Search StackOverflow for many questions and answers on finding the first Monday of a week.
LocalDate (date-only)
While you might well be tempted to use LocalDate objects (date only, no time-of-day) to build an Interval. That would be sensible and useful. Unfortunately, the implementation of Interval supports only DateTime objects, not LocalDate.
YearWeek Class
The ThreeTen-Extra project has a class for YearQuarter. The original question looks like it is asking for a YearWeek class. Work has been done to add such a class to ThreeTen-Extra.
Another possibility is to use the class CalendarWeek in my library Time4J. Example:
CalendarWeek now = SystemClock.inLocalView().now(CalendarWeek.chronology());
int actual = now.getWeek(); // 35 on 2016-08-29
int max = now.getMaximum(CalendarWeek.WEEK_OF_YEAR); // 52 in year 2016
This class is also modelled as date interval and can be converted to a common DateInterval (from Monday to Sunday) offering stream support via its method toFlexInterval().
Related
Hi i am trying to get the current year in the below code however it is returning a 1970 year instead of 2020 last month this was working correctly but since we in January 2020, it is now returning a date from 1970, please assist
public String firstDateOfNextMonth(){
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
Calendar today = Calendar.getInstance();
Calendar next = Calendar.getInstance();
today.clear();
Date date;
next.clear();
next.set(Calendar.YEAR, today.get(Calendar.YEAR));
next.set(Calendar.MONTH, today.get(Calendar.MONTH)+ 1);
next.set(Calendar.DAY_OF_MONTH, 1);
date = next.getTime();
Log.d(TAG, "The Date: " + dateFormat.format(date));
return dateFormat.format(date);
}
If you have Java 8 or above, then you have java.time and you won't have to rely on outdated datetime implementations and you can do it this way:
public static String getFirstOfNextMonth() {
// get a reference to today
LocalDate today = LocalDate.now();
// having today,
LocalDate firstOfNextMonth = today
// add one to the month
.withMonth(today.getMonthValue() + 1)
// and take the first day of that month
.withDayOfMonth(1);
// then return it as formatted String
return firstOfNextMonth.format(DateTimeFormatter.ISO_LOCAL_DATE);
}
which prints the following when called today (2020-01-03) like System.out.println(getFirstOfNextMonth());:
2020-02-01
You might have to involve an external library, the ThreeTenAbp if you want it to work in Android below API level 26. Its use is explained in this question.
not sure why the today date gets cleared, remove today.clear() at line 4
today.clear(); initalize all elements of a date with the value 0
removing this line will give you the right answer
tl;dr
LocalDate // Represent a date-only value without a time-of-day and without a time zone.
.now( // Determine the current date as seen through the wall-clock time used by people in certain region (a time zone).
ZoneId.of( "America/Montreal" ) // Real time zone names have names in the format of `Continent/Region`. Never use 2-4 letter pseudo-zones such as `IST`, `PST`, or `CST`, which are neither standardized nor unique.
) // Return a `LocalDate`.
.with( // Move from one date another by passing a `TemporalAdjuster` implementation.
TemporalAdjusters // Class providing several implementations of `TemporalAdjuster`.
.firstDayOfNextMonth() // This adjuster finds the date of the first of next month, as its name suggests.
) // Returns another `LocalDate` object. The original `LocalDate` object is unaltered.
.toString() // Generate text in standard ISO 8601 format of YYYY-MM-DD.
See this code run live at IdeOne.com.
2020-02-01
Details
You are using terrible date-time classes that were made obsolete years ago by the unanimous adoption of JSR 310 defining the java.time classes.
The Answer by deHaar is correct. Here is an even shorter solution.
TemporalAdjuster
To move from one date to another, the java.time classes include the TemporalAdjuster interface. Pass one of these objects to the with method found on many of the other java.time classes.
TemporalAdjusters.firstDayOfNextMonth()
Several implementations of that interface are found in the class TemporalAdjusters (note the s plural). One of those is firstDayOfNextMonth(), just what you need.
Get today's date. A time zone is required, as for any given moment the date varies around the globe by time zone. If omitted, your JVM's current default time zone is implicitly applied. Better to be explicit.
ZoneId z = ZoneId.of( "Asia/Tokyo" ) ;
LocalDate today = LocalDate.now( z ) ;
Get your TemporalAdjuster object.
TemporalAdjuster ta = TemporalAdjusters.firstDayOfNextMonth() ;
Apply that adjuster to get another LocalDate object. Note that java.time classes are immutable by design. So we get a new object rather than altering the original.
LocalDate firstOfNextMonth = today.with( ta ) ;
We can shorten this code to a one-liner, if desired.
LocalDate firstOfNextMonth =
LocalDate
.now(
ZoneId.of( "Africa/Tunis" )
)
.with(
TemporalAdjusters.firstDayOfNextMonth()
)
;
Text
Your desired output format of YYYY-MM-DD complies with the ISO 8601 standard used by default in the java.time classes when parsing/generating text. So no formatting pattern need be specified.
String output = firstOfNextMonth.toString() ;
2020-02-01
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
You are using Calendar.clear() which clears all the fields of your calendar, and essentially reverts it to 1/1/1970 (epoch time 0).
remove today.clear() and you'll get the correct answer
see more here
Remove next.clear();. As Calendar next= Calendar.getInstance(); initiates next with the current date, in your cases Fri Jan 03 2020 15:07:53. And when you do next.clear(), it sets to the inital epoch.
Epoch, also known as Unix timestamps, is the number of seconds (not
milliseconds!) that have elapsed since January 1, 1970 at 00:00:00 GMT
(1970-01-01 00:00:00 GMT).
So, I'm trying to basically take 2 DateTime objects and set them to the first day of their respective months so that I can ultimately calculate the months between the two dates.
Example of the code:
DateTime dt = new DateTime();
DateTime newDT = dt.withDayOfMonth(1);
And before anyone asks, the actual code coverts a Date object into a DateTime object which is used in another section of the code.
The issue is, when I do this in a unit test it seems to work just fine. However, when I try to test this using SOAP UI I can see in the course of debugging that I'm getting a runtime exception due to:
method lookup failed for selector "withDayOfMonth" with signature "(I)Lorg/joda/time/DateTime;"
In the corresponding server.txt log file, I can see a stack trace which indicates a no such method has occured.
After further research, I've found that our app server currently employs an outdated version of the JodaTime jar (1.2.1), while my eclipse library contains the correct jar (1.6.2).
However, now the question becomes what's the best way to accomplish my goal here (to create a new DateTime object with the first day of the month set to 0) since I don't have access to the withDayOfMonth method provided by JodaTime?
tl;dr
LocalDate firstOfThisMonth =
LocalDate.now( ZoneId.of( "America/Montreal" ) )
.with( TemporalAdjusters.firstDayOfMonth() ) ;
Details
Other answers address your Joda-Time question. However, the Joda-Time project is now in maintenance mode, with the team advising migration to the java.time classes. So here is a solution in java.time code.
LocalDate
The LocalDate class represents a date-only value without time-of-day and without time zone.
A time zone is crucial in determining a date. For any given moment, the date varies around the globe by zone. For example, a few minutes after midnight in Paris France is a new day while still “yesterday” in Montréal Québec.
ZoneId z = ZoneId.of( "America/Montreal" );
LocalDate today = LocalDate.now( z );
TemporalAdjuster
The TemporalAdjuster interface in java.time provides for classes to manipulate a value. The TemporalAdjusters class (note the plural s) provides several handy implementations of adjusters. One is firstDayOfMonth.
LocalDate firstOfThisMonth = today.with( TemporalAdjusters.firstDayOfMonth() ) ;
firstOfThisMonth.toString(): 2016-03-01
Period
The Period class tracks a span of time not attached to the timeline. It keeps a number of years, months, and days.
LocalDate start = LocalDate.of ( 2016 , 1 , 1 ) ;
LocalDate stop = LocalDate.of ( 2016 , 3 , 1 ) ;
Period p = Period.between ( start , stop ) ;
Calling toString on a Period generates a string in standard ISO 8601 format.
P2M
You can ask for one part as a number, such as number of months.
int months = p.getMonths();
2
Note that the elapsed time shown here wisely uses the Half-Open approach where the beginning is inclusive while the ending is exclusive.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, .Calendar, & java.text.SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to java.time.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
Where to obtain the java.time classes?
Java SE 8 and SE 9 and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
The ThreeTenABP project adapts ThreeTen-Backport (mentioned above) for Android specifically.
See How to use….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
A possible way to do so, using another method for Joda-Time API which is present in 1.2.1 version it's the follow:
DateTime dateTime = new DateTime().dayOfMonth().withMinimumValue();
Another approach could be to use jdk Calendar to set the first day of the month for a date. And then get the joda DateTime using DateTime(Calendar cal) constructor:
Calendar cal = Calendar.getInstance();
cal.set(Calendar.DAY_OF_MONTH, 1);
DateTime dateTime = new DateTime(cal);
However probably as other answer suggest the best you can do is update your Joda-Time version.
Calculate months from difference of the two month values. For example if newDate is 1st July 2016 and oldDate is 31st May 2016, newDate.getMonth() will return 7 and oldDate.getMonth() will return 5, and the difference will be rounded up as required.
int months = newDate.getMonth() - oldDate.getMonth(); // 7 - 5 = 2
Given LocalDate I want to convert to week number since Epoch
One way to do that is:
LocalDate date = datePicker.getValue(); // input from your date picker
Locale locale = Locale.US;
int weekOfYear = date.get(WeekFields.of(locale).weekOfWeekBasedYear());
And X = find weeks since Epoch to prevYear
And then result = X + weekOfYear
Although someway we can find out "week number since epoch", is there clean solution to find it using Java 8 ?
UPDATE:
Even above solution wont work as one week(always starting from Sunday) can span across two years
I am turning the comment by Tunaki into an Answer.
tl;dr
ChronoUnit.WEEKS.between (
LocalDate.ofEpochDay ( 0 ) ,
LocalDate.now( ZoneOffset.UTC )
)
LocalDate
The LocalDate class represents a date-only value without time-of-day and without time zone.
A time zone is crucial in determining a date. For any given moment, the date varies around the globe by zone. For example, a few minutes after midnight in Paris France is a new day while still “yesterday” in Montréal Québec.
Here we use UTC as the time zone for today’s date, in accordance with the epoch being defined in UTC.
LocalDate today = LocalDate.now( ZoneOffset.UTC );
ChronoUnit
The ChronoUnit class has methods for calculating an elapsed number of years or months or weeks or such. For weeks it simply takes the number of days and divides by 7. So the first day-of-week is irrelevant. Not sure if that meets your needs or not as the Question is vague.
LocalDate today = LocalDate.now ( ZoneOffset.UTC ); // Using UTC to match the definition of the Java epoch.
LocalDate epoch = LocalDate.ofEpochDay ( 0 );
long weeks = ChronoUnit.WEEKS.between ( epoch , today );
Dump to console.
System.out.println ( "epoch: " + epoch + " | today in UTC: " + today + " | weeks: " + weeks );
epoch: 1970-01-01 | today in UTC: 2016-08-29 | weeks: 2434
You could adjust your starting date to a specific day-of-week if that makes sense for your needs. I'm not sure if that fits because the Question is not clear.
LocalDate epoch = LocalDate.ofEpochDay ( 0 );
LocalDate start = epoch.with ( TemporalAdjusters.nextOrSame ( DayOfWeek.SUNDAY ) ); // Get the first Sunday that *is* the epoch or *follows* the epoch.
long weeks = ChronoUnit.WEEKS.between ( start , today );
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
There's a method out there for milliseconds since epoch for a Date:
long timeInMillis = date.getTime();
and apparently there are 604800000 milliseconds to a week. So I guess you could go
long weeksSinceEpoch = date.getTime() / 604800000;
I think you'd probably actually need to add 1 to your final value though, since Java truncates longs.
Hello I'm trying to convert a string in the format "17:50" to a date in android but when I try to run this code I get the correct hour from the string but the full date is from 1970. I need this date to schedule some local notifications on a given time of the day or in the next day.
String dtStart = "17:50";
SimpleDateFormat format = new SimpleDateFormat("H:mm");
try {
Calendar cal = Calendar.getInstance();
Date date = format.parse(dtStart);
cal.setTime(date);
System.out.println(cal.getTime());
} catch (ParseException e) {
e.printStackTrace();
}
Thu Jan 01 17:50:00 BRT 1970
It's not an error, your code works well. Just if you want to get current date, you have to add the difference between current day and 1st of January 1970.
Your parsed date gives you 17:30 hours, which means 17 * 60 * 60 * 1000 ms + 30 * 60 + 1000 ms.
This way you can find current day: https://stackoverflow.com/a/1908419/4142087
What Anton suggested was correct, and the current day / next day logic is your custom implementation. You have to check current time and if it past that time, jump to setting up the alarm the next day.
java.time
You need a time-of-day class to represent your intended meaning. The legacy date-time classes from the earliest versions of Java lack such a class. The java.sql.Time class pretends to do this, but actually contains a date as well due to poor design decisions.
LocalTime
You want the LocalTime class for a time-of-day value without a date and without a time zone.
It uses a generic 24-hour single-day clock. Adding/subtracting spans of time wraps around the clock since it lacks any concept of dates.
Define a formatting pattern to match your input string.
DateTimeFormatter f = DateTimeFormatter.ofPattern( "H:mm" ) ; // Uppercase `H` means 24-hour clock, lowercase `h` means 12-hour clock.
Parse input string.
String input = "7:50" ;
LocalTime lt = LocalTime.parse( input , f ) ;
Generate a string in standard ISO 8601 format.
String output = lt.toString() ;
07:50
Perhaps your business logic requires assigning the time-of-day to a date. To determine a moment, a point on the timeline, you must also specify a time zone.
LocalDate ld = LocalDate.of( 2018 , Month.MARCH , 27 ) ;
ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime zdt = ZonedDateTime.of( ld , lt , z ) ;
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
This question already has answers here:
Why dec 31 2010 returns 1 as week of year?
(6 answers)
Closed 5 years ago.
I'm trying to understand how java.util.Calendar.get(java.util.Calendar.WEEK_OF_YEAR) works, but it seems that I'm missing some points.
String time = "1998-12-31"; // year month day
java.util.Calendar date = java.util.Calendar.getInstance();
date.setTime((new java.text.SimpleDateFormat("yyyy-MM-dd")).parse(time));
System.err.println("Week of year = " + date.get(java.util.Calendar.WEEK_OF_YEAR));
// Week of year = 1 Why ???
Why date.get(java.util.Calendar.WEEK_OF_YEAR) returns 1 for the last week of the year?
Moreover, WEEK_OF_YEAR for "1998-01-01" is 1 and for "1998-12-23" it is 52.
Does anybody have an explanation for this behavior?
From java.util.Calendar javadoc:
First Week
Calendar defines a locale-specific seven day week using two
parameters: the first day of the week and the minimal days in first
week (from 1 to 7). These numbers are taken from the locale resource
data when a Calendar is constructed. They may also be specified
explicitly through the methods for setting their values.
When setting or getting the WEEK_OF_MONTH or WEEK_OF_YEAR fields,
Calendar must determine the first week of the month or year as a
reference point. The first week of a month or year is defined as the
earliest seven day period beginning on getFirstDayOfWeek() and
containing at least getMinimalDaysInFirstWeek() days of that month or
year. Weeks numbered ..., -1, 0 precede the first week; weeks numbered
2, 3,... follow it. Note that the normalized numbering returned by
get() may be different. For example, a specific Calendar subclass may
designate the week before week 1 of a year as week n of the previous
year.
So it's locale-specific. In your case, if the week contains days from new year, it is counted as week 1 from the new year.
You can change this behavior by using Calendar#setMinimalDaysInFirstWeek(int).
tl;dr
java.time.LocalDate.parse( "1998-12-31" )
.get( IsoFields.WEEK_OF_WEEK_BASED_YEAR )
53
Or, add a library, and then…
org.threeten.extra.YearWeek.from( // Convert from a `LocalDate` object to a `YearWeek` object representing the entire week of that date’s week-based year.
LocalDate.parse( "1998-12-31" ) // Parse string into a `LocalDate` objects.
).getWeek() // Extract an integer number of that week of week-based-year, either 1-52 or 1-53 depending on the year.
53
Details
I'm trying to understand how java.util.Calendar.get(java.util.Calendar.WEEK_OF_YEAR) works
Don’t! That class is a bloody mess, and best left forgotten.
The answer by npe is correct. In Calendar, the definition of a week varies by locale. A well-intentioned feature, but confusing.
Standard week definition
There are many ways to define “a week” and “first week of the year”.
However, there is one major standard definition: the ISO 8601 standard. That standard defines weeks of the year, including the first week of the year.
the week with the year's first Thursday
A standard week begins with Monday and ends with Sunday.
Week # 1 of a standard week-based-year has the first Thursday of the calendar-year.
java.time
The java.time classes supplanted the troublesome legacy date-time classes. These modern classes support the ISO 8601 week through the IsoFields class, holding three constants that implement TemporalField:
WEEK_OF_WEEK_BASED_YEAR
WEEK_BASED_YEAR
WEEK_BASED_YEARS
Call LocalDate::get to access the TemporalField.
LocalDate ld = LocalDate.parse( "1998-12-31" ) ;
int weekOfWeekBasedYear = ld.get( IsoFields.WEEK_OF_WEEK_BASED_YEAR ) ;
int yearOfWeekBasedYear = ld.get( IsoFields.WEEK_BASED_YEAR ) ;
ld.toString(): 1998-12-31
weekOfWeekBasedYear: 53
yearOfWeekBasedYear: 1998
Notice the day after, the first day of the new calendar year 1999, also is in the same week, week # 53 of week-based 1998.
LocalDate firstOf1999 = ld.plusDays( 1 );
int weekOfWeekBasedYear_FirstOf1999 = firstOf1999.get( IsoFields.WEEK_OF_WEEK_BASED_YEAR ) ;
int yearOfWeekBasedYear_FirstOf1999 = firstOf1999.get( IsoFields.WEEK_BASED_YEAR ) ;
firstOf1999.toString(): 1999-01-01
weekOfWeekBasedYear_FirstOf1999: 53
yearOfWeekBasedYear_FirstOf1999: 1998
ISO 8601 string format
The ISO 8601 standard defines a textual format as well as a meaning for week-based-year values: yyyy-Www. For a specific date, add day-of-week numbered 1-7 for Monday-Sunday: yyyy-Www-d.
Construct such a string.
String outputWeek = ld.format( DateTimeFormatter.ISO_WEEK_DATE ) ; // yyyy-Www
1998-W53
String outputDate = outputWeek + "-" + ld.getDayOfWeek().getValue() ; // yyyy-Www-d
1998-W53-4
YearWeek
This work is much easier if you add the ThreeTen-Extra library to your project. Then use the YearWeek class.
YearWeek yw = YearWeek.from( ld ) ; // Determine ISO 8601 week of a `LocalDate`.
Generate the standard string.
String output = yw.toString() ;
1998-W53
And parse.
YearWeek yearWeek = YearWeek.parse( "1998-W53" ) ;
yearWeek.toString(): 1998-W53
Determine a date. Pass a java.time.DayOfWeek enum object for day-of-week Monday-Sunday.
LocalDate localDate = yw.atDay( DayOfWeek.MONDAY ) ;
localDate.toString(): 1998-12-28
I strongly recommending adding this library to your project. Then you can pass around smart objects rather than dumb ints. Doing so makes your code more self-documenting, provides type-safety, and ensures valid values.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
Using a JDBC driver compliant with JDBC 4.2 or later, you may exchange java.time objects directly with your database. No need for strings nor java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android, the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
Joda-Time
UPDATE: The Joda-Time project is now in maintenance mode, with the team advising migration to the java.time classes. This section left intact as history.
The excellent Joda-Time framework uses ISO 8601 for its defaults. Its classes include this week-of-year information. Joda-Time is a popular replacement for the notoriously troublesome java.util.Date & java.util.Calendar classes bundled with Java.
Example Code
Here is some example code to get first moment of the first day of the first week of the year of the current date-time.
Note the call to withTimeAtStartOfDay to get the first moment of the day.
DateTimeZone timeZone = DateTimeZone.forID( "Europe/Paris" );
DateTime now = new DateTime( timeZone );
DateTime firstWeekStart = now.withWeekOfWeekyear(1).withDayOfWeek(1).withTimeAtStartOfDay();
DateTime firstWeekStop = firstWeekStart.plusWeeks( 1 );
Interval firstWeek = new Interval( firstWeekStart, firstWeekStop );
Dump to console…
System.out.println( "now: " + now );
System.out.println( "firstWeekStart: " + firstWeekStart );
System.out.println( "firstWeekStop: " + firstWeekStop );
System.out.println( "firstWeek: " + firstWeek );
When run…
now: 2014-02-07T12:49:33.623+01:00
firstWeekStart: 2013-12-30T00:00:00.000+01:00
firstWeekStop: 2014-01-06T00:00:00.000+01:00
firstWeek: 2013-12-30T00:00:00.000+01:00/2014-01-06T00:00:00.000+01:00