I'm using following code. Values (hour, minute, second, date, month, year, day_of_week) are hardcoded for simplicity.
Calendar c = Calendar.getInstance();
c.set(Calendar.HOUR_OF_DAY, 10);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.DATE, 2);
c.set(Calendar.MONTH, Calendar.APRIL);
c.set(Calendar.YEAR, 2018);
c.setFirstDayOfWeek(Calendar.MONDAY);
c.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
String date = String.format(Locale.US, "%1$tA %1$tb %1$td %1$tY at %1$tI:%1$tM %1$Tp", c);
Log.e("date", date);
Observation 1
This code prints the the following on the Titanium S1 (Android 4.1.2)
E/date: Monday Mar 26 2018 at 10:00 AM
This code prints the the following on the Moto G4+ (Android 7.0), Moto G5+ (Android 7.0)
E/date: Monday Apr 02 2018 at 10:00 AM
Observation 2
This difference appears only on 2nd April & 2nd July to my knowledge. I am seeing the first day of month is Sunday for both the April and July month. It works fine for rest of the days
Question
1) Does the first day of month (MONDAY) cause this difference? Changing it to SUNDAY seems working on these two days. But I am not sure this works in all the scenarios.
This is affecting users on my app. Please help. Thanks.
I've made some test in JDK 7 and the results don't seem to be affected by setFirstDayOfWeek. Actually, it seems to be related to the JVM default locale (java.util.Locale).
When the default locale is Japanese or Thai, the final result is March. Somehow, the JVM default locale affects the internals of Calendar, in some misterious ways that, I must admit, are beyond my understanding. But anyway, one way to fix it is to set in the Calendar the same locale you used in the output:
Calendar c = Calendar.getInstance(Locale.US);
This fixed the issue for me.
java.time API
This bizarre behaviour is one of the reasons to not use the old Calendar API (there are many others, btw).
In Android, it's possible to use the java.time API. In API level 26, the java.time classes are available. For lower levels, you can use this backport - and in this link there are instuctions to configure it in Android.
With this new API, it's much easier - and less error prone - to get what you want. To build a specific date/time, use a ZonedDateTime class, and a DateTimeFormatter to convert it to a String in a specific format:
// April 2nd 2018, 10 AM
ZonedDateTime dt = ZonedDateTime.of(2018, 4, 2, 10, 0, 0, 0, ZoneId.systemDefault());
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("EEEE MMM dd yyyy 'at' hh:mm a", Locale.US);
String formattedDate = dt.format(fmt); // Monday Apr 02 2018 at 10:00 AM
Note that the day of week is adjusted automatically, based on the day/month/year values provided.
To get the current date/time, just use ZonedDateTime.now(ZoneId.systemDefault()) (to get the current date/time in JVM's default timezone), or use ZoneId.of("zone name") to use a specific timezone - replace "zone name" with any valid IANA zone's names.
Related
TL;DR: I'm getting a strange result when setting a GregorianCalendar with a custom Julian->Gregorian cutover date. 1 Jan 1800 becomes 12 Jan 1800, where 1800 is before the custom cutover date (31 Jan 1918) but after the default cutover date (15 Oct 1582). (This does not happen before the default cutover date or on the default calendar.)
This is part of a bigger algorithm where I want to use GregorianCalendar to calculate certain dates in a year, hoping to benefit by having the Julian/Gregorian leap year calculations be transparent to me, since the dates may be either before or after the cutover date.
I'm trying to use the following properties of GregorianCalendar, quoted from GregorianCalendar API docs:
GregorianCalendar is a hybrid calendar that supports both the Julian and Gregorian calendar systems with the support of a single discontinuity, which corresponds by default to the Gregorian date when the Gregorian calendar was instituted (October 15, 1582 in some countries,
later in others). The cutover date may be changed by the caller by calling setGregorianChange().
Before the Gregorian cutover, GregorianCalendar implements the Julian calendar. The only difference between the Gregorian and the Julian calendar is the leap year rule.
Prior to the institution of the Gregorian calendar, New Year's Day was March 25. To avoid confusion, this calendar always uses January 1.
To replicate the base problem, here is a basic Java main() method. I create 2 calendars, one the default Gregorian, and another one a Gregorian with the cutover date when Russia adopted it, i.e. 31 Jan 1918. Then I set both calendars to 1 Jan 1800. The "Russian" calendar changes this date to 12 Jan 1800, as shown when printed out immediately after the set.
public static void main(String[] args) {
DateFormat DF = DateFormat.getDateInstance(DateFormat.SHORT);
System.out.printf("Generic Gregorian Calendar (after cutover)%n");
GregorianCalendar genericCal = new GregorianCalendar();
System.out.printf(" Changeover=%s%n", DF.format(genericCal.getGregorianChange()));
genericCal.set(1800, Calendar.JANUARY, 1);
System.out.printf("%3s %s%n", "", DF.format(genericCal.getTime()));
System.out.printf("Russian Gregorian Calendar (before cutover)%n");
GregorianCalendar russianCal = new GregorianCalendar();
russianCal.setGregorianChange(new GregorianCalendar(1918, Calendar.JANUARY, 31).getTime());
System.out.printf(" Changeover=%s%n", DF.format(russianCal.getGregorianChange()));
russianCal.set(1800, Calendar.JANUARY, 1);
System.out.printf("%3s %s%n", "", DF.format(russianCal.getTime()));
for (int i = 1; i < 15; i++) {
russianCal.add(Calendar.DAY_OF_YEAR, -1);
System.out.printf("%3d: %s %n", -i, DF.format(russianCal.getTime()));
}
}
This outputs:
Generic Gregorian Calendar (after cutover)
Changeover=1582/10/15
1800/01/01
Russian Gregorian Calendar (before cutover)
Changeover=1918/01/31
1800/01/12
-1: 1800/01/11
-2: 1800/01/10
-3: 1800/01/09
-4: 1800/01/08
-5: 1800/01/07
-6: 1800/01/06
-7: 1800/01/05
-8: 1800/01/04
-9: 1800/01/03
-10: 1800/01/02
-11: 1800/01/01
-12: 1799/12/31
-13: 1799/12/30
-14: 1799/12/29
The 11-day difference looks similar to the days lost when the Julian/Gregorian switchover would be made, but that would only apply during the period in 1918 after 31 Jan, in this instance (31 Jan was followed by 14 Feb in 1918 in Russia, which is a 13-day difference).
I would appreciate any explanation, or help to get the date set to what I intend.
Unfortunately I'm stuck with standard Java 8 libraries, no 3rd party libraries possible at this time. Also, it seems the new java.time classes won't "automatically" help with the Julian/Gregorian transition (if I'm wrong I welcome pointers), so my Plan B would be to simply do the calculations without using any date classes.
Your code behaves correctly though confusingly, I agree. For formatting your Russian date you need to instruct your formatter to use the Russian Gregorian crossover date.
Setting your Russian calendar to 1 Jan 1800 works. There is no change happening.
GregorianCalendar russianCal = new GregorianCalendar();
russianCal.setGregorianChange(
new GregorianCalendar(1918, Calendar.JANUARY, 31).getTime());
russianCal.set(1800, Calendar.JANUARY, 1);
System.out.println("Russian month (0-based): "
+ russianCal.get(Calendar.MONTH));
System.out.println("Russian day of month: "
+ russianCal.get(Calendar.DATE));
Output is:
Russian month (0-based): 0
Russian day of month: 1
The date is 1 Jan as it should (Calendar confusingly uses 0 for January).
When you take out the time into a Date, you get a normal java.util.Date, there’s nothing Russian nor Julian about it. The Date object represents some time of the day that is 1 Jan in Russia and 12 Jan in the Gregorian calendar, in your default time zone. When you further format this Date using a DateFormat with default settings, is uses the standard Gregorian crossover date in 1582 and therefore prints 12 Jan. To print 1 Jan as in the Russian calendar you need to instruct the formatter to use the Russian Gregorian crossover date. You do this by passing it a GregorianCalendar that uses the desired crossover date:
Date dateCorrespondingToRussianCal = russianCal.getTime();
System.out.println("Corresponding java.util.Date: "
+ dateCorrespondingToRussianCal);
DateFormat russianDateFormatter
= DateFormat.getDateInstance(DateFormat.SHORT);
russianDateFormatter.setCalendar(russianCal);
System.out.println("Formatted Russian date: "
+ russianDateFormatter.format(dateCorrespondingToRussianCal));
Output in my time zone:
Corresponding java.util.Date: Sun Jan 12 13:10:30 CET 1800
Formatted Russian date: 1/1/00
Other options?
Unfortunately you are correct: java.time, the modern Java date and time API, has no support for the Julian calendar out of the box. I read that you could not use any 3rd party libraries. Options to consider include:
For other readers that may use a 3rd party library: Joda-Time and it GJChronology. It
Implements the Gregorian/Julian calendar system which is the calendar system used in most of the world. …
For yourself: You may develop your own Julian-Gregorian chronology to use with java.time. It would require an effort, but I would expect it to give a beautiful result.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Documentation of Joda-Time’s GJChronology
I'm using java.util.Date. The expression:
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2016-10-16 00:00:00").toString()
is returning "Sun Oct 16 01:00:00 BRST 2016" (a wrong date) for this particular date, but a correct response on most other dates.
I also tried a format string taken from Oracle docs: "yyyy-MM-dd'T'HH:mm:ss.SSSZ" and a specific date: "2016-10-16T00:00:00.000-0300" and got the same "error" (I suppose), one hour ahead:
Sun Oct 16 01:00:00 BRST 2016
That usually happens due to Daylight Saving Time (DST) (also known as "summer time"). Based on the date/time, and on the output you've got (Sun Oct 16 01:00:00 BRST 2016), I suppose it's Brazil's DST (BRST is the abbreviation for Brazilian's summer time).
SimpleDateFormat uses the JVM's default timezone (if you don't specify one), so probably your default zone is America/Sao_Paulo or Brazil/East (you can check that by calling TimeZone.getDefault().getID()).
In America/Sao_Paulo timezone, DST started at October 16th 2016: at midnight, clocks shifted 1 hour forward from midnight to 1 AM (and the offset changed from -03:00 to -02:00). So all local times between 00:00 and 00:59 didn't exist in this timezone (you can also think that clocks changed from 23:59:59.999999999 directly to 01:00).
That's why this specific date in midnight (which didn't exist in this timezone) is automatically shifted to the next valid moment (1 AM). But if I set a specific timezone in the formatter, this won't happen:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// set formatter to use UTC (instead of JVM default timezone)
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
// parse it as midnight (no shift to 01:00)
Date date = sdf.parse("2016-10-16 00:00:00");
In this case, I'm using UTC, which has no DST effects. But remind that the date created above will be equivalent to midnight in UTC (which is the same instant as October 15th 2016 at 9 PM in Brazil (the day before) - maybe it's not what you want).
Be aware of that before changing the timezone: if you want a specific instant (a precise point in time), changing the timezone will affect the final result. If you just want to consider the date/time values and don't care about in what timezone it is (treating the value as it's a "local date/time"), just set the formatter to use UTC, to avoid DST effects (an ugly workaround, IMO, but just because the java.util.Date API doesn't have specific types for local date/times).
But anyway, this is not an error. It's the expected behaviour (DST and timezones have lots of strange and non-intuitive behaviours, but that's the way it is).
Java new Date/Time API
The old classes (Date, Calendar and SimpleDateFormat) have lots of problems and design issues, and they're being replaced by the new APIs.
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 6 or 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, you'll also need 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.
This new API has lots of new types that best suit different use-cases. In your case, if you want just the date and time fields and don't care about timezones, you can use a LocalDateTime. To parse it, just use a DateTimeFormatter:
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime dt = LocalDateTime.parse("2016-10-16 00:00:00", fmt);
System.out.println(dt); // 2016-10-16T00:00
This will ignore DST effects, because a LocalDateTime has no timezone information.
But if you want to consider the timezone, you can convert this to a ZonedDateTime, using a ZoneId to get the timezone:
// convert to a timezone
ZonedDateTime z = dt.atZone(ZoneId.of("America/Sao_Paulo"));
System.out.println(z); // 2016-10-16T01:00-02:00[America/Sao_Paulo]
In this case, note that the time was adjusted to 1 AM, because I converted to America/Sao_Paulo timezone, so the DST effects were considered, as already explained above.
With this new API we can look closer at what's happening in this specific timezone, for this particular date/time. First I'll create a ZonedDateTime that corresponds to October 15th 2016, at 23:59:59 in America/Sao_Paulo timezone, and then I'll add 1 second to it:
// October 15th 2016, at 23:59:59 in Sao Paulo timezone
ZonedDateTime z = ZonedDateTime.of(2016, 10, 15, 23, 59, 59, 0, ZoneId.of("America/Sao_Paulo"));
System.out.println(z); // 2016-10-15T23:59:59-03:00[America/Sao_Paulo]
System.out.println(z.plusSeconds(1)); // 2016-10-16T01:00-02:00[America/Sao_Paulo]
Note that the original date is in offset -03:00 (3 hours behind UTC, which is the standard offset for America/Sao_Paulo timezone). One second later, it should be midnight, but due to DST change, the clock shifts directly to 1 AM, and the offset changes to -02:00.
Even if I try to directly create October 16th 2016 at midnight in this timezone, the value will be corrected, because this local time doesn't exist in this timezone, due to DST shift:
// Try to create October 16th 2016, at midnight in Sao Paulo timezone
ZonedDateTime z = ZonedDateTime.of(2016, 10, 16, 0, 0, 0, 0, ZoneId.of("America/Sao_Paulo"));
System.out.println(z); // 2016-10-16T01:00-02:00[America/Sao_Paulo]
So, it's not an error. October 16th 2016 at midnight in America/Sao_Paulo timezone doesn't exist due to a DST change, and the API's automatically corrects this to the next valid moment (which is, in this case, 1 AM).
The API uses IANA timezones names (always in the format Region/City, like America/Sao_Paulo or Europe/Berlin).
Avoid using the 3-letter abbreviations (like CST or PST) because they are ambiguous and not standard.
You can get a list of available timezones (and choose the one that fits best your system) by calling ZoneId.getAvailableZoneIds().
You can also use the system's default timezone with ZoneId.systemDefault(), but this can be changed without notice, even at runtime, so it's better to explicity use a specific one.
Your problem is probably the timezone, so you could use:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
Here is the original answer.
Sometimes the date object gives me the wrong day of the week. My code is extremely simple.
//Setting to 2017, April, 12th
Date dt = new Date(2017, 3, 11);
System.out.printf("%TA", dt);
This gives me 'WEDNESDAY' which is correct. In fact it seems to work fine for any year in the recent past and near future.
However, if I set the time to 2999, August, 5th. Which is
//Setting to 2999, August, 5th
Date dt = new Date(2999, 7, 4);
System.out.printf("%TA", dt);
It returns 'TUESDAY' when actually August the 5th 2999 is a Monday.
Why is this happening? And how do I get around it?
Cheers!
Why is this happening?
The important part of your answer is already in the comments. That deprecatred constructor is confusing alright. You managed to get the month correct, which a lot of people don’t; you got the year wrong and the day-of-month wrong. Since you are using System.out.printf(), you may use it for checking too:
// Setting to 2017, April, 12th -- NOT
Date dt = new Date(2017, 3, 11);
System.out.printf("%1$tm %1$te,%1$tY%n", dt);
This prints
04 11,3917
What a coincidence that it happens to be a Wednesday as you had expected. I would suspect this coincidence to hold for years 2001 through 2099. It doesn’t hold in February 2000. February 12 that year was a Saturday, but:
// Incorrect attempt at setting to 2000, February, 12th
dt = new Date(2000, 1, 11);
System.out.printf("%TA%n", dt);
This prints
SUNDAY
And how do I get around it?
If you can use Java 8 or later, I recommend you stay away not only from the deprecated constructor, but from the Date class altoghether. These days we have a bunch of better alternatives.
// Setting to 2999, August, 5th
LocalDate ld = LocalDate.of(2999, Month.AUGUST, 5);
System.out.println(ld);
System.out.printf("%TA", ld);
This prints the correct
2999-08-05
MONDAY
I usually use a DateTimeFormatter with the new classes, but LocalDate works with System.out.printf() too as just shown. According to the docs it works with TemporalAccessor, which includes DayOfWeek, Instant, IsoEra, LocalDate, LocalDateTime, LocalTime, Month, MonthDay, OffsetDateTime, OffsetTime, Year, YearMonth, ZonedDateTime, ZoneOffset and more.
I have a GregorianCalendar instance for Tuesday September 2nd. The valñue is checked in milliseconds and is OK. I want another calendar which is the next Sunday (7th) at 23:59:59. So:
GregorianCalendar currentCalendar = MyClock.INSTANCE.getCurrentCalendar();
GregorianCalendar nextSunday =
(GregorianCalendar)currentCalendar.clone();
// GregorianCalendar uses Sunday as first day of week, so we must
// advance one week
int currentWeek = nextSunday.get(GregorianCalendar.WEEK_OF_YEAR);
nextSunday.set(GregorianCalendar.WEEK_OF_YEAR,
currentWeek + this.THIS_WEEK);
nextSunday.set(GregorianCalendar.DAY_OF_WEEK, GregorianCalendar.SUNDAY);
nextSunday.set(GregorianCalendar.HOUR_OF_DAY, 23);
nextSunday.set(GregorianCalendar.MINUTE, 59);
nextSunday.set(GregorianCalendar.SECOND, 59);
nextSunday.set(GregorianCalendar.MILLISECOND, 0);
So, since sunday is day number 1 of the week for the GregorianCalendar, and I am at week of year number 36, I add one week and then set the day to sunday.
The real problems comes now: when I execute in my development machine, with OpenJDK 1.7.0_55, it works perfectly. If I go to my test machine with OpenJDK 1.7.0_51, it does it all wrong:
Adds one week until tuesday 9th, and then goes to sunday 14th instead of sunday 7th.
I don't know if I am doing it right or wrong: what is really killing me is that the result depends on the machine, and I haven't found any difference at GregorianCalendar at those OpenJDK versions. Any explanation for this behaviour?
PD: Please stick to GregorianCalendar. I know is a bit shitty, but I don't want to use Joda Calendar or any other at current stage of development.
EDIT: I found method setWeekDate(year, week_of_year, day_of_week). One would think that setting year, week and day of week into the same method will grant it will succeed. It does not: still going from 2nd to 14th. What monkey wrote this?
I've made slight alterations to your code:
SimpleDateFormat sdf = new SimpleDateFormat("dd MM yyyy - HH:mm:ss.SSSS Z");
GregorianCalendar currentCalendar = (GregorianCalendar) Calendar.getInstance();
currentCalendar.set(Calendar.DAY_OF_MONTH, 2);
System.out.println(sdf.format(currentCalendar.getTime()));
GregorianCalendar nextSunday = (GregorianCalendar) currentCalendar.clone();
// GregorianCalendar uses Sunday as first day of week, so we must
// advance one week
int currentWeek = nextSunday.get(GregorianCalendar.WEEK_OF_YEAR);
nextSunday.set(GregorianCalendar.WEEK_OF_YEAR, currentWeek + 1);
nextSunday.set(GregorianCalendar.DAY_OF_WEEK, GregorianCalendar.SUNDAY);
nextSunday.set(GregorianCalendar.HOUR_OF_DAY, 23);
nextSunday.set(GregorianCalendar.MINUTE, 59);
nextSunday.set(GregorianCalendar.SECOND, 59);
nextSunday.set(GregorianCalendar.MILLISECOND, 0);
System.out.println(sdf.format(nextSunday.getTime()));
This outputs:
02 12 2014 - 19:40:46.0250 +0200
07 12 2014 - 23:59:59.0000 +0200
Which is correct. However, I have two things to point out:
check the value of this.THIS_WEEK. I have substituted it for the value 1 and it works ok on my machine.
check the timezone on both machines (in my case GMT+2). Since both machines use the same code which both initialize the values and use them, there shouldn't really be problems. But if you use the values as milliseconds on a different machine (e.g. exposing the value through a webservice or something), you might hit problems.
I would try using java.util.Calendar method add(int field, int amount) instead.
nextSunday.add(Calendar.DAY_OF_MONTH,5). One line of code instead of three.
I have a date in the string that looks like MON 07:15. I'm trying to parse this to a Date using this snippet of code:
System.out.println(new SimpleDateFormat("E kk:mm").parse("MON 07:15"));
Using the above code, prints a date that reads:
Mon Jan 05 07:15:00 EET 1970
I'd like the parse the date string to the next upcoming date. At the time of posting, my local date and time is Fri Aug 08 11:45:00 EEST 2014 and the next Monday will be on the 11th so the resultant date that I'm looking for is Mon Aug 11 07:15:00 EEST 2014. How can I parse this?
The day and time object that I'll be parsing will always be in the future.
I would separate parsing from everything else.
Parse the value as you're already doing, which gives you a date in the wrong year.
From that, take the time of day and the day of week - those are the important things. You can throw everything else away.
Next, take the current date and time, and perform whatever operations you need to in order to get to the right date/time. You should consider:
What time zone are you interested in?
If the current time is 06:00 on a Monday and you've been asked for 07:15 on a Monday, does that mean today or next Monday?
If the current time is 08:00 on a Monday and you've been asked for 07:15 on a Monday, does that mean today or next Monday?
By separating out the parsing from the computations, you can make the testing simpler two: test each of the operations separately. I'd advise using a clock abstraction of some kind to indicate "an object which can get you the current instant in time" - that way you can test all kinds of combinations of "desired day/time" and "current day/time".
Ideally, use java.time from Java 8 or Joda Time - both are much nicer than the java.util.* API.
Are you looking something like following?
DateFormat df = new SimpleDateFormat("E kk:mm");
Date date = df.parse("MON 07:15");
Date today = new Date();
Calendar calendar = Calendar.getInstance();
Calendar calendar1 = Calendar.getInstance();
calendar.setTime(today);
calendar1.setTime(date);
if (calendar.get(Calendar.DAY_OF_WEEK) == calendar1.get(Calendar.DAY_OF_WEEK)) {
String time = df.format(today);
Date t1 = df.parse(time);
if (t1.before(date)) {
calendar.set(Calendar.HOUR, calendar1.get(Calendar.HOUR));
calendar.set(Calendar.MINUTE, calendar1.get(Calendar.MINUTE));
calendar.set(Calendar.SECOND, 0);
System.out.println(calendar.getTime());
} else {
calendar.add(Calendar.DATE, 7);
calendar.set(Calendar.HOUR_OF_DAY, calendar1.get(Calendar.HOUR));
calendar.set(Calendar.MINUTE, calendar1.get(Calendar.MINUTE));
calendar.set(Calendar.SECOND, 0);
System.out.println(calendar.getTime());
}
} else {
int toDay = calendar.get(Calendar.DAY_OF_WEEK);
int givenDay = calendar1.get(Calendar.DAY_OF_WEEK);
int count = 7 - toDay + givenDay;
calendar.add(Calendar.DAY_OF_MONTH, count);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.HOUR_OF_DAY, calendar1.get(Calendar.HOUR));
calendar.set(Calendar.MINUTE, calendar1.get(Calendar.MINUTE));
System.out.println(calendar.getTime());
}
Out put:
Mon Aug 11 07:15:00 IST 2014
Leave me a comment telling me whether I got your question correct.
This answer addresses the second part, getting the next logical date from today.
Avoid .Date/.Calendar
The java.util.Date & .Calendar classes bundled with Java are notoriously troublesome. Avoid them.
Joda-Time or java.time
I suggest learning how to use a sophisticated date-time library. In Java that means either:
Joda-Time
java.time (built into Java 8, inspired by Joda-Time).
Time Zone
The time zone is crucial in determining the day and day-of-week. Use proper time zone names, never the 3 or 4 letter codes.
If you ignore time zone, the JVM’s current default time zone will be applied implicitly. This means different outputs when moving your app from one machine to another, or when a sys admin changes the time zone of host machine, or when any Java code in any thread of any app within the same JVM decides to call setDefault even during your app‘s execution.
Example Code To Get Next Day-Of-Week
Here is example code using Joda-Time 2.7.
Get the time zone you desire/expect. If working in UTC, use the constant DateTimeZone.UTC.
DateTimeZone zone = DateTimeZone.forID( "America/Montreal" );
Get the date-time value you need. Here I am using the current moment.
DateTime dateTime = DateTime.now( zone );
Specify the future day-of-week you want. Note that Joda-Time uses the sensible # 1 for first day of week, rather than zero-based counting found in java.util.Calendar. First day of week is Monday, per international norms and standards (not Sunday as is common in United States).
int dayOfWeek = DateTimeConstants.SATURDAY;
The withDayOfWeek command may go back in time. So we use a ternary operator (?:) to make sure we go forwards in time by adding a week as needed.
DateTime future = ( dateTime.getDayOfWeek() < dayOfWeek )
? dateTime.withDayOfWeek( dayOfWeek )
: dateTime.plusWeeks( 1 ).withDayOfWeek( dayOfWeek );
You may want to adjust the time-of-day to the first moment of the day to emphasize the focus on the day rather than a particular moment within the day.
future = future.withTimeAtStartOfDay(); // Adjust time-of-day to first moment of the day to stress the focus on the entire day rather than a specific moment within the day. Or use `LocalDate` class.
Dump to console.
System.out.println( "Next day # " + dayOfWeek + " after " + dateTime + " is " + future );
When run.
Next day # 6 after 2015-04-18T16:03:36.146-04:00 is 2015-04-25T00:00:00.000-04:00