I expect these two formatters to be equivalent:
DateTimeFormatter fromBuilder = new DateTimeFormatterBuilder()
.appendValue(IsoFields.WEEK_BASED_YEAR, 4)
.appendLiteral('-')
.appendValue(IsoFields.WEEK_OF_WEEK_BASED_YEAR, 2)
.toFormatter();
DateTimeFormatter fromPattern = DateTimeFormatter.ofPattern("YYYY-ww");
But they do not give the same result:
LocalDate date = LocalDate.of(2017, 1, 1);
System.out.printf("from builder: %s%n", fromBuilder.format(date)); // prints 'from builder: 2016-52'
System.out.printf("from pattern: %s%n", fromPattern.format(date)); // prints 'from pattern: 2017-01'
What am I missing?
The Y and w patterns correspond to a localized version of week-fields, using the JVM's default locale (java.util.Locale). The second formatter is equivalent to:
// localized week fields (using default Locale)
WeekFields weekFields = WeekFields.of(Locale.getDefault());
// equivalent to YYYY-ww
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
.appendValue(weekFields.weekBasedYear(), 4)
.appendLiteral('-')
.appendValue(weekFields.weekOfWeekBasedYear(), 2)
.toFormatter();
As this is locale dependent, it can or can't work like IsoFields. The WeekFields created above will have a different behaviour depending on the JVM's default locale.
IsoFields, on the other hand, follows ISO-8601 definition to define the week-based fields, as described in the javadoc:
The first week of a week-based-year is the first Monday-based week of the standard ISO year that has at least 4 days in the new year.
If January 1st is Monday then week 1 starts on January 1st
If January 1st is Tuesday then week 1 starts on December 31st of the previous standard year
If January 1st is Wednesday then week 1 starts on December 30th of the previous standard year
If January 1st is Thursday then week 1 starts on December 29th of the previous standard year
If January 1st is Friday then week 1 starts on January 4th
If January 1st is Saturday then week 1 starts on January 3rd
If January 1st is Sunday then week 1 starts on January 2nd
As 2017-01-01 is a Sunday, it corresponds to the last line above: week 1 starts on January 2nd 2017, so January 1st 2017 is still in the last week of 2016.
You can check how your WeekFields instance differs from IsoFields by calling the methods getFirstDayOfWeek() and getMinimalDaysInFirstWeek() - which are used to calculate the values of the respecitive week-based fields:
A week is defined by:
The first day-of-week. For example, the ISO-8601 standard considers Monday to be the first day-of-week.
The minimal number of days in the first week. For example, the ISO-8601 standard counts the first week as needing at least 4 days.
Together these two values allow a year or month to be divided into weeks.
In the JVM I'm using, the default locale is pt_BR, and the WeekFields created has the first day-of-week as Sunday, and minimal days in first week as 1. Check yours and you'll see that it also differs from IsoFields.
You can check ISO's definition by using the constant WeekFields.ISO: getFirstDayOfWeek() returns Monday and getMinimalDaysInFirstWeek() returns 4.
Also, remind that there's a small difference between IsoFields and WeekFields.ISO. Quoting JodaStephen's comment in this thread:
The only observable difference was that WeekFields operates on all calendar systems (by converting to ISO) whereas IsoFields only operates on ISO (and rejects other calendar systems)
Related
My first print shows that daylight saving starts from 8th March, whereas to check that I have passed my custom date(9th March) to timeZone.inDaylightTime(calendar.getTime()), whereas it is returning false.Where I am going wrong ?
TimeZone timeZone = TimeZone.getTimeZone("US/Eastern");
System.out.println("TimeZone ::: "+timeZone);
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.AM_PM, Calendar.AM);
calendar.set(Calendar.MONTH, Calendar.MARCH);
calendar.set(Calendar.DAY_OF_MONTH, 9);
calendar.set(Calendar.YEAR,2019);
boolean b = timeZone.inDaylightTime(calendar.getTime());
System.out.println("Is the day in daylight saving "+b);
DST wasn't in effect in the U.S. Eastern zone on March 9th, 2019. It went into effect March 10th at 2 a.m. See timeanddate.com.
My first print shows that daylight saving starts from 8th March...
I'm guessing you're basing that on this:
startMode=3,
startMonth=2,
startDay=8,
startDayOfWeek=1,
startTime=7200000
Remember that DST changes (in the U.S. at least) always happen early on a Sunday morning. What that rule says is that the first Sunday on or after March 8th is when the change should occur, not that it should occur on March 8th (which was a Friday). The law says it starts the second Sunday in March (at the moment, it's varied periodically), so by saying the first Sunday on or after March 8th, the code fits the definition "second Sunday of March."
You can see this concept in the description of SimpleTimeZone:
To construct a SimpleTimeZone with a daylight saving time schedule, the schedule can be described with a set of rules, start-rule and end-rule. A day when daylight saving time starts or ends is specified by a combination of month, day-of-month, and day-of-week values. The month value is represented by a Calendar MONTH field value, such as Calendar.MARCH. The day-of-week value is represented by a Calendar DAY_OF_WEEK value, such as SUNDAY. The meanings of value combinations are as follows.
Exact day of month
To specify an exact day of month, set the month and day-of-month to an exact value, and day-of-week to zero. For example, to specify March 1, set the month to MARCH, day-of-month to 1, and day-of-week to 0.
Day of week on or after day of month
To specify a day of week on or after an exact day of month, set the month to an exact month value, day-of-month to the day on or after which the rule is applied, and day-of-week to a negative DAY_OF_WEEK field value. For example, to specify the second Sunday of April, set month to APRIL, day-of-month to 8, and day-of-week to -SUNDAY.
Day of week on or before day of month
To specify a day of the week on or before an exact day of the month, set day-of-month and day-of-week to a negative value. For example, to specify the last Wednesday on or before the 21st of March, set month to MARCH, day-of-month is -21 and day-of-week is -WEDNESDAY.
Last day-of-week of month
To specify, the last day-of-week of the month, set day-of-week to a DAY_OF_WEEK value and day-of-month to -1. For example, to specify the last Sunday of October, set month to OCTOBER, day-of-week to SUNDAY and day-of-month to -1.
could you please post a small example by using those ZoneId and
ZonedDateTime to get dayLightSaving date exists
It’ll be my pleasure.
ZoneId zone = ZoneId.of("America/New_York");
ZonedDateTime testTime = ZonedDateTime.of(2019, 3, 9, 0, 0, 0, 0, zone);
boolean dst = zone.getRules().isDaylightSavings(testTime.toInstant());
System.out.println("Is the day in daylight saving " + dst);
Is the day in daylight saving false
As stated in the other answer, summer time (DST) began last Sunday, March 10, in North America this year. So let’s try with that day at 5 AM — then we should get true.
ZonedDateTime testTime = ZonedDateTime.of(2019, 3, 10, 5, 0, 0, 0, zone);
Is the day in daylight saving true
zone.getRules() returns a ZoneRules object. Such an object knows everything about the UTC offset for the time zone, also about DST. So we can query it with an Instant. We get an Instant from testTime.toInstant().
Link: Section Time Zone and Offset Classes in the Oracle tutorial (including an example of using ZoneRules that somewhat resembles mine).
I'm trying to set minimal value of WEEK_OF_MONTH field as follows:
calendar.set(WEEK_OF_MONTH, calendar.getActualMinimum(WEEK_OF_MONTH));
The call to
calendar.getActualMinimum(WEEK_OF_MONTH)
returns 0
But at calculation during get* operations this field becomes 5.
Moreover, without leniency mode, I get
java.lang.IllegalArgumentException: WEEK_OF_MONTH: 0 -> 5 // or MONTH: 9 -> 8
at java.util.GregorianCalendar.computeTime(GregorianCalendar.java:2829)
at java.util.Calendar.updateTime(Calendar.java:3393)
at java.util.Calendar.complete(Calendar.java:2265)
at java.util.Calendar.get(Calendar.java:1826)
at Main.main(Main.java:19)
If I set WEEK_OF_MONTH = 1, then I get it correctly as 1.
Check out an example
Can anyone clarify such a behavior? Thanks in advance.
java.time
Locale russia = Locale.forLanguageTag("ru-RU");
WeekFields wf = WeekFields.of(russia);
LocalDate date = LocalDate.now(ZoneId.of("Europe/Moscow"));
int minimumWeekOfMonth = date.with(TemporalAdjusters.firstDayOfMonth()).get(wf.weekOfMonth());
System.out.println("Minimum week of month: " + minimumWeekOfMonth);
LocalDate dateInFirstWeekOfMonth = date.with(wf.weekOfMonth(), minimumWeekOfMonth);
System.out.println("Date in first week of month: " + dateInFirstWeekOfMonth);
When running this snippet just now I got the following output:
Minimum week of month: 1
Date in first week of month: 2018-10-05
I have assumed that you are in Russian locale. Russia uses the international week numbering where Monday is the first day of the week and week one of a year or month is the first week that contains at least 4 days of the year or month. So week 1 of October 2018 was from Monday October 1 through Sunday October 7. This in turn means that the minumum week in this month is 1. Starting out from today (a Friday) and setting the week of month to 1 gives Friday in week 1, that is, Friday October 5.
If I start out from Wednesday September 12 instead I get:
Minimum week of month: 0
Date in first week of month: 2018-08-29
Week 1 of September was from Monday September 3 through September 9. This means that September 1 and 2 were in week 0, so 0 is the minimum week of month for September. And when starting from a Wednesday I set week number to 0, I get the Wednesday of that week, which happens to lie in August: August 29. If we ask for the week of month of that date, do we get 0?
System.out.println("Week of month: " + dateInFirstWeekOfMonth.get(wf.weekOfMonth()));
Output:
Week of month: 5
Since the date is in August, we now get which week of August the date is in, which happens to be week 5.
What happened in your code?
It seems to me that GregorianCalendar.getActualMinimum(Calendar.WEEK_OF_MONTH) always returns 0. I cannot make sense of this observation. Since Russia uses the Gregorian calendar, an instance of GregorianCalendar is what you really get from Calendar.getInstance.
I wouldn’t want to bother. As I said in a comment already, the Calendar class is long outdated and has a range of design problems with it, so I recommend you don’t use it. I’d certainly prefer java.time, the modern Java date and time API, any time.
Link
Oracle tutorial: Date Time explaining how to use java.time.
As per https://en.wikipedia.org/wiki/ISO_8601#Week_dates, Weekdays start on Monday. But, from Java, if you try to extract week number in two different ways, two different outputs come if the date is a Sunday.
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
public class TestDate {
public static void main(String[] args) throws ParseException {
final SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
final SimpleDateFormat weekFormatter = new SimpleDateFormat("ww");
String date = "2018-10-21";
System.out.println(weekFormatter.format(formatter.parse(date)));
Calendar calendar = Calendar.getInstance();
calendar.setFirstDayOfWeek(Calendar.MONDAY);
calendar.setTime(formatter.parse(date));
System.out.println(calendar.get(Calendar.WEEK_OF_YEAR));
}
}
Output:
43
42
Is this an inconsistency?
This is just a test program I wrote to reproduce the issue, I noticed the problem in Hive, like the following:
0: jdbc:hive2://zk0-something> select from_unixtime(t, 'ww'), weekofyear(from_unixtime(t, 'yyyy-MM-dd')) from (select 1540122033 as t) a;
+------+------+--+
| _c0 | _c1 |
+------+------+--+
| 43 | 42 |
+------+------+--+
1 row selected (0.388 seconds)
0: jdbc:hive2://zk0-something>
java.time
String date = "2018-10-21";
LocalDate ld = LocalDate.parse(date);
int weekOfYear = ld.get(WeekFields.ISO.weekOfYear());
System.out.println(weekOfYear);
Output:
42
Since you are interested in the ISO 8601 rules for week numbers, use WeekFields.ISO for getting week related data from a LocalDate. You may also use a formatter if you like:
DateTimeFormatter weekFormatter = DateTimeFormatter.ofPattern("ww", Locale.FRANCE);
System.out.println(ld.format(weekFormatter));
Output is the same:
42
The locale passed to DateTimeFormatter.ofPattern determines the week scheme. If I pass Locale.US instead, I get 43.
I recommend you use java.time, the modern Java date and time API, and stay away from the old date-time classes like SimpleDateFormat and Calendar. The old ones were poorly designed and the modern ones are much nicer to work with.
What went wrong in your code?
Both the outdated SimpleDateFormat class and the modern DateTimeFormatter take their week numbering scheme from their locale. If no locale is specified for the formatter, it uses the default locale of the JVM. So if the JVM has American locale, for example, the formatter will print 43 in your first example because in the US Sunday October 21 this year was in week 43. If the locale is French, it will print 42 because that day was in week 42 in France. France follows the ISO 8601 standard, the USA does not.
In your example, setting the Calendar’s first day of week to Monday causes the week number to be 42 as you had expected. This will not always be the case, however. Week numbers are defined not only by the first day of the week but also by the definition of week 1. From your link:
The first ISO week of a year may have up to three days that are
actually in the Gregorian calendar year that is ending; if they are
Monday, Tuesday and Wednesday. Similarly, the last ISO week of a year
may have up to three days that are actually in the Gregorian calendar
year that is starting; if they are Friday, Saturday, and Sunday. The
Thursday of each ISO week is always in the Gregorian calendar year
denoted by the ISO week-numbering year.
The American definition of which week is week 1 is different: In the US January 1 is always in week 1. Therefore if your Calendar is created with American locale, setting its first day of week to Monday is not enough to make is follow ISO 8601 rules. Coincidentally, for 2018 the week numbers agree, though.
I need to get the date by passing these parameters
year
week number (in a month) i.e. 1,2,3,4,5
day number (in a week) 0 (Sunday) to 6 (Saturday)
Month
I looked for a constructor in Calendar class but does not contain these parameters.
In spite of your tags I agree with Joe C’s comment, you should prefer the modern Java date and time API (AKA known as java.time or JSR-310) over the long outdated Calendar and friends. The modern classes are more programmer friendly and nicer to work with, they come with fewer surprises, lead to clearer and more natural code (at least when you get used to the fluent style) and are easier to debug should you make a bug.
I don’t know how you count your weeks of the month. I have assumed that the first week starts on the first of the month and lasts 7 days (so the second week starts on the 8th and the 5th week on the 29th day of the month). If you count differently, please see Hugo’s answer.
I suggest this method:
public static LocalDate weekNoAndDayToDate(Year year, Month month, int weekOfMonth, int dayOfWeek) {
// find day of week: convert from 0 (Sunday) to 6 (Saturday)
// to DayOfWeek, from 1 (Monday) to 7 (Sunday)
DayOfWeek dow;
if (dayOfWeek == 0) { // Sunday
dow = DayOfWeek.SUNDAY;
} else {
dow = DayOfWeek.of(dayOfWeek);
}
return year.atMonth(month)
.atDay(1)
.with(TemporalAdjusters.nextOrSame(dow))
.with(ChronoField.ALIGNED_WEEK_OF_MONTH, weekOfMonth);
}
With this we may do for example
LocalDate today = weekNoAndDayToDate(Year.of(2017), Month.JULY, 1, 1);
This yields
2017-07-03
If you need either a Date or a GregorianCalendar, for example for an old API that you cannot change, do one of the following:
Date oldfashiondDateObject
= Date.from(today.atStartOfDay(ZoneId.systemDefault()).toInstant());
GregorianCalendar oldfashionedCalendarObject
= GregorianCalendar.from(today.atStartOfDay(ZoneId.systemDefault()));
In both cases the result will be different in different time zones (one of the inherent troubles of the old classes). On my computer the first yields a Date of
Mon Jul 03 00:00:00 CEST 2017
The second yields a GregorianCalendar equal to the same point in time.
To create date from year, week number and week's day use java.util.Calendar instance:
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, 2017);
cal.set(Calendar.WEEK_OF_YEAR, 26);
cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
To convert from Calendar to java.util.Date :
Date date = cal.getTime();
To convert Date into java.time.LocalDateTime use :
LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
As #Ole V.V. explained, you need to define in what day the week starts, and how many days it must have to be considered the first week.
For example, the ISO-8601 standard considers:
Monday to be the first day-of-week.
The minimal number of days in the first week: the standard counts the first week as needing at least 4 days
The month is divided into periods where each period starts on the defined first day-of-week. The earliest period in the same month is referred to as week 0 if it has less than the minimal number of days and week 1 if it has at least the minimal number of days.
Depending on how you define those, you can have different results. Consider the calendar for July 2017:
July 2017
Su Mo Tu We Th Fr Sa
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
If we consider ISO's definition, we have:
week zero - 2017-07-01 to 2017-07-02
week 1: from 2017-07-03 to 2017-07-09
week 2: from 2017-07-10 to 2017-07-16
week 3: from 2017-07-17 to 2017-07-22
week 4: from 2017-07-23 to 2017-07-30
week 5: 2017-07-31
If we consider first day of week as Sunday and minimal number of days in the first week as 1, we have:
week 1: 2017-07-01
week 2: from 2017-07-02 to 2017-07-08
week 3: from 2017-07-09 to 2017-07-15
week 4: from 2017-07-16 to 2017-07-21
week 5: from 2017-07-22 to 2017-07-29
week 6: from 2017-07-30 to 2017-07-31
As a solution with Calendar was already posted, I'm gonna write one using the new API. If you're using Java 8, consider using the new java.time API. It's easier, less bugged and less error-prone than the old APIs.
If you're using Java <= 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's the ThreeTenABP (more on how to use it here).
The code below works for both.
The only difference is the package names (in Java 8 is java.time and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp), but the classes and methods names are the same.
I'm using a DateTimeFormatter because it takes all the fields (month, year, day of week and week of month) and resolves the day accordingly, creating a LocalDate (which has the day/month/year fields). I'm also using the WeekFields class, which can be configured to use different week definitions (first day and minimal number of days in first week)
There's also a little adjustment to consider Sunday as zero:
public LocalDate getDate(int weekOfMonth, int dayOfWeek, int month, int year) {
// you can customize your week definition (first day of week and mininum days in first week)
WeekFields wf = WeekFields.of(DayOfWeek.SUNDAY, 2);
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
// week of month, using week definition from WeekFields
.appendValue(wf.weekOfMonth()).appendPattern(" ")
// day of week
.appendValue(ChronoField.DAY_OF_WEEK)
// month and year
.appendPattern(" M/yyyy")
// create formatter
.toFormatter();
return LocalDate.parse(weekOfMonth + " " +
// Sunday is 0, adjusting value
(dayOfWeek == 0 ? 7 : dayOfWeek) + " " + month + "/" + year, fmt);
}
Using this code (week starts on Sunday, and 2 days are required to be considered the first week - otherwise week will be zero as in the first example above):
LocalDate d = getDate(1, 6, 7, 2017);
d will be 2017-07-08 (Saturday in the week 1 of July 2017).
If you want to use ISO 8601 definition, use the constant WeekFields.ISO instead of using WeekFields.of() method.
As #Ole V.V. suggested in the comments, it can also be done without creating a formatter: get the first dayOfWeek of the month and adjust it to the desired weekOfMonth:
public LocalDate getDate(int weekOfMonth, int dayOfWeek, int month, int year) {
// you can customize your week definition (first day of week and mininum days in first week)
WeekFields wf = WeekFields.of(DayOfWeek.SUNDAY, 2);
// Sunday is 0, adjusting value
DayOfWeek dow = DayOfWeek.of(dayOfWeek == 0 ? 7 : dayOfWeek);
// get the first weekday of the month
LocalDate first = LocalDate.of(year, month, 1).with(TemporalAdjusters.nextOrSame(dow));
// check in which week this date is
int week = first.get(wf.weekOfMonth());
// adjust to weekOfMonth
return first.plusWeeks(weekOfMonth - week);
}
This works exactly the same way, but without the need of a formatter - I've tested with dates from year 1600 to 2100 and it gets the same results.
PS: Calendar also has a similar configuration via the methods setFirstDayOfWeek() and setMinimalDaysInFirstWeek(). You can check the default configuration calling the respective get methods.
DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern("yyyyww");
dateTimeFormatter.print(new DateTime(2016, 1, 1, 1, 1).withZone(DateTimeZone.UTC))
returns 201653
Why is it 53 week of 2016 rather than 2015?
What you are looking for is the week-based-year (symbol x), not the year of era (symbol y). See also the pattern syntax used by Joda-Time which deviates from that of SimpleDateFormat or Java-8. So the solution should look like:
DateTimeFormatter f = DateTimeFormat.forPattern("xxxxww");
String s = f.print(new DateTime(2016, 1, 1, 1, 1).withZone(DateTimeZone.UTC));
System.out.println(s); // 201553
Because January 1st was Friday. And in that case, that week counts as last week of 2015, and the first week of 2016 will start on first Monday (January 4th).
That is implemented in accordance with ISO 8601 standard:
There are several mutually equivalent and compatible descriptions of week 01:
- the week with the year's first Thursday in it (the formal ISO definition),
- the week with 4 January in it,
- the first week with the majority (four or more) of its days in the starting year, and
- the week starting with the Monday in the period 29 December – 4 January.