Inconsistent behaviour of GregorianCalendar when created from ZonedDateTime - java
I'm writing some integration of legacy code that expects date/time as a GregorianCalendar.
So, I'm creating the GregorianCalendar using from(ZonedDateTime) method.
However, the result is showing some inconsistent behaviour when the GregorianCalendar is created from the ZonedDateTime versus when the GregorianCalendar is created, let's say, from a string parsing or even from epoch millis. For example, when setting the Calendar day_of_week. To demonstrate this (https://onecompiler.com/java/3yssz4q86):
import java.util.*;
import java.time.*;
public class Main {
public static void main(String[] args) {
final GregorianCalendar calendarPure = new GregorianCalendar(TimeZone.getTimeZone("America/Los_Angeles"));
printInfo(calendarPure);
ZoneId zone = ZoneId.of("America/Los_Angeles");
final ZonedDateTime zdtNow = ZonedDateTime.now(zone);
final GregorianCalendar calendarFromZdt = GregorianCalendar.from(zdtNow);
printInfo(calendarFromZdt);
final GregorianCalendar calendarFromZdtEpoch = new GregorianCalendar();
calendarFromZdtEpoch.setTimeInMillis(zdtNow.toInstant().toEpochMilli());
printInfo(calendarFromZdtEpoch);
}
private static void printInfo(GregorianCalendar cal) {
System.out.printf("Now: %tc%n", cal);
System.out.printf("Last Sunday: %tc%n", getLastSunday(cal));
System.out.printf("Week of Year: %d%n", cal.get(Calendar.WEEK_OF_YEAR));
System.out.printf("Time in Millis: %d%n", cal.getTimeInMillis());
System.out.printf("toString(): %s%n", cal);
System.out.println();
}
public static GregorianCalendar getLastSunday(GregorianCalendar referenceDate) {
final GregorianCalendar lastSundayMidNight = (GregorianCalendar)referenceDate.clone();
lastSundayMidNight.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
return lastSundayMidNight;
}
}
The output is:
Now: Wed Dec 28 16:45:36 PST 2022
Last Sunday: Sun Dec 25 16:45:36 PST 2022
Week of Year: 53
Time in Millis: 1672274736631
toString(): java.util.GregorianCalendar[time=1672274736631,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2022,MONTH=11,WEEK_OF_YEAR=53,WEEK_OF_MONTH=5,DAY_OF_MONTH=28,DAY_OF_YEAR=362,DAY_OF_WEEK=4,DAY_OF_WEEK_IN_MONTH=4,AM_PM=1,HOUR=4,HOUR_OF_DAY=16,MINUTE=45,SECOND=36,MILLISECOND=631,ZONE_OFFSET=-28800000,DST_OFFSET=0]
Now: Wed Dec 28 16:45:36 PST 2022
Last Sunday: Sun Jan 01 16:45:36 PST 2023
Week of Year: 52
Time in Millis: 1672274736668
toString(): java.util.GregorianCalendar[time=1672274736668,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=2,minimalDaysInFirstWeek=4,ERA=1,YEAR=2022,MONTH=11,WEEK_OF_YEAR=52,WEEK_OF_MONTH=5,DAY_OF_MONTH=28,DAY_OF_YEAR=362,DAY_OF_WEEK=4,DAY_OF_WEEK_IN_MONTH=4,AM_PM=1,HOUR=4,HOUR_OF_DAY=16,MINUTE=45,SECOND=36,MILLISECOND=668,ZONE_OFFSET=-28800000,DST_OFFSET=0]
Now: Thu Dec 29 00:45:36 UTC 2022
Last Sunday: Sun Dec 25 00:45:36 UTC 2022
Week of Year: 53
Time in Millis: 1672274736668
toString(): java.util.GregorianCalendar[time=1672274736668,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Etc/UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2022,MONTH=11,WEEK_OF_YEAR=53,WEEK_OF_MONTH=5,DAY_OF_MONTH=29,DAY_OF_YEAR=363,DAY_OF_WEEK=5,DAY_OF_WEEK_IN_MONTH=5,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=45,SECOND=36,MILLISECOND=668,ZONE_OFFSET=0,DST_OFFSET=0]
I noticed that, from GregorianCalendar.toString() that prints the internal fields a different in firstDayOfWeek and minimalDaysInFirstWeek. That could be the cause for the divergence in the after results. I wonder where the GregorianCalendar is getting these values from. Maybe there is a way to set something on ZonedDateTime object that will ensure the results will be the same? Couldn't find anything on javadocs.
Note: Please, don't tell me not to use GregorianCalendar. I know, I know. I'm just writing an integration for two pieces of code that I'm not allowed to modify, one uses GregorianCalendar (sigh), the other uses ZonedDateTime.
Related
Get same date in different timezone
I have a epochmilli sec time. Created a date object from this epoch time. Date date = new Date(epochMilli); Suppose date is "23 Nov 2019 00:00:00" Now, I just want to get this same date in different timezones like :] Japan time : 23 Nov 2019 00:00:00 US time : 23 Nov 2019 00:00:00 I am currently using LocalDateTime or ZonedDateTime. But when I am converting into different zone, time also changes. But I don't want this time to change. Thanks in Advance.
Unfortunately, you didn't show us how you used ZonedDateTime, so the following examples may cover more than you just wanted by showing how to parse millis, convert the resulting date time from one zone to others and how to parse a date time using different zones: public static void main(String[] args) { long epochMillis = 1574208000000L; // define a formatter to be used DateTimeFormatter dtf = DateTimeFormatter.ofPattern("dd MMM yyyy HH:mm:ss", Locale.ENGLISH); // create an instant from the milliseconds Instant instant = Instant.ofEpochMilli(epochMillis); // and create some example time zones for later use ZoneId utc = ZoneId.of("UTC"); ZoneId tokyo = ZoneId.of("Asia/Tokyo"); ZoneId losAngeles = ZoneId.of("America/Los_Angeles"); ZoneId chicago = ZoneId.of("America/Chicago"); /* * Part 1: Getting the date time converted to different time zones */ // use the instant to create a ZonedDateTime at a specific time zone, here: UTC ZonedDateTime utcZdt = instant.atZone(utc); // then take the UTC-ZonedDateTime as base for conversion to other time zones ZonedDateTime asiaTokyoConvertedfromUtc = utcZdt.withZoneSameInstant(tokyo); ZonedDateTime americaLosAngelesConvertedfromUtc = utcZdt.withZoneSameInstant(losAngeles); ZonedDateTime americaChicagoConvertedfromUtc = utcZdt.withZoneSameInstant(chicago); // print the results System.out.println("#### 1574208000000L at UTC, converted to other zones ####"); System.out.println("UTC time zone:\t\t\t\t" + utcZdt.format(dtf)); System.out.println("JST (Japan/Tokyo) time zone:\t\t" + asiaTokyoConvertedfromUtc.format(dtf)); System.out.println("PST (USA/Los Angeles) time zone:\t" + americaLosAngelesConvertedfromUtc.format(dtf)); System.out.println("CST (USA/Chicago) time zone:\t\t" + americaChicagoConvertedfromUtc.format(dtf)); System.out.println(); /* * Part 2: Getting the date time in different time zones */ // use the instant to create a ZonedDateTime at Asia/Tokyo ZonedDateTime asiaTokyoFromMillis = instant.atZone(tokyo); // use the instant to create a ZonedDateTime at America/Los Angeles ZonedDateTime americaLosAngelesFromMillis = instant.atZone(losAngeles); // use the instant to create a ZonedDateTime at America/Chicago ZonedDateTime americaChicagoFromMillis = instant.atZone(chicago); // print the (expected) results, same as converted date times... System.out.println("#### 1574208000000L at different zones ####"); System.out.println("UTC time zone:\t\t\t\t" + utcZdt.format(dtf)); System.out.println("JST (Asia/Tokyo) time zone:\t\t" + asiaTokyoFromMillis.format(dtf)); System.out.println("PST (USA/Los Angeles) time zone:\t" + americaLosAngelesFromMillis.format(dtf)); System.out.println("CST (USA/Chicago) time zone:\t\t" + americaChicagoFromMillis.format(dtf)); System.out.println(); /* * Part 3: How to parse the date time instead of millis */ // provide a parseable date time String String dateTime = "23 Nov 2019 00:00:00"; // parse it in each desired time zone ZonedDateTime utc23Nov2019 = LocalDateTime.parse(dateTime, dtf).atZone(utc); ZonedDateTime asiaTokyo23Nov2019 = LocalDateTime.parse(dateTime, dtf) .atZone(tokyo); ZonedDateTime americaChicago23Nov2019 = LocalDateTime.parse(dateTime, dtf) .atZone(losAngeles); ZonedDateTime americaLosAngeles23Nov2019 = LocalDateTime.parse(dateTime, dtf) .atZone(chicago); // print the results, now you have the 23. Nov 2019 at 00:00:00 at each time zone System.out.println("#### \"23 Nov 2019 00:00:00\" at different zones ####"); System.out.println("UTC time zone:\t\t\t\t" + utc23Nov2019.format(dtf)); System.out.println("JST (Asia/Tokyo) time zone:\t\t" + asiaTokyo23Nov2019.format(dtf)); System.out.println("PST (USA/Los Angeles) time zone:\t" + americaChicago23Nov2019.format(dtf)); System.out.println("CST (USA/Chicago) time zone:\t\t" + americaLosAngeles23Nov2019.format(dtf)); } The output of this is #### 1574208000000L at UTC, converted to other zones #### UTC time zone: 20 Nov 2019 00:00:00 JST (Japan/Tokyo) time zone: 20 Nov 2019 09:00:00 PST (USA/Los Angeles) time zone: 19 Nov 2019 16:00:00 CST (USA/Chicago) time zone: 19 Nov 2019 18:00:00 #### 1574208000000L at different zones #### UTC time zone: 20 Nov 2019 00:00:00 JST (Asia/Tokyo) time zone: 20 Nov 2019 09:00:00 PST (USA/Los Angeles) time zone: 19 Nov 2019 16:00:00 CST (USA/Chicago) time zone: 19 Nov 2019 18:00:00 #### "23 Nov 2019 00:00:00" at different zones #### UTC time zone: 23 Nov 2019 00:00:00 JST (Asia/Tokyo) time zone: 23 Nov 2019 00:00:00 PST (USA/Los Angeles) time zone: 23 Nov 2019 00:00:00 CST (USA/Chicago) time zone: 23 Nov 2019 00:00:00
The legacy java.util.Date is not time-zone-aware. It is basically a point in time measured in milliseconds since 1970-1-1 0:00. Nothing else. When a date is print, it is formatted using your system's default locale and time zone. In order to get a java.util.Date object formatted for a different time zone you have to configure your DateFormat accordingly. Date pointInTime = new Date(); DateFormat dateFormat = DateFormat.getDateTimeInstance(); dateFormat.setTimeZone(TimeZone.getTimeZone("PST")); System.out.println(dateFormat.format(pointInTime)); dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); System.out.println(dateFormat.format(pointInTime)); With the newer LocalDate API, things are different. The previous answer gives a rich example.
Different result in parsing datetime via calendsr or simpleDateFormatter
I now working with java 1.6 and encounter strange behaviour, may be bug, here is code: import org.junit.Test; import javax.xml.bind.DatatypeConverter; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; public class TestDate { #Test public void testConvert() throws Exception { Calendar parsedCalendar = DatatypeConverter.parseDateTime("0001-01-01T00:00:00"); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); Date sdfDate = simpleDateFormat.parse("0001-01-01T00:00:00"); Calendar parsedCalendar2 = DatatypeConverter.parseDateTime("1980-03-01T00:00:00"); SimpleDateFormat simpleDateFormat2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); Date sdfDate2 = simpleDateFormat2.parse("1980-03-01T00:00:00"); System.out.println("parsedCalendar: " + parsedCalendar.getTimeInMillis()); System.out.println("parsedCalendar TZ: " + parsedCalendar.getTimeZone()); System.out.println("parsedCalendar Date: " + parsedCalendar.getTime()); System.out.println("sdfDate: " + sdfDate); System.out.println("sdfDate millis: " + sdfDate.getTime()); System.out.println("parsedCalendar2: " + parsedCalendar2.getTimeInMillis()); System.out.println("parsedCalendar2 TZ: " + parsedCalendar2.getTimeZone()); System.out.println("parsedCalendar2 Date: " + parsedCalendar2.getTime()); System.out.println("sdfDate2: " + sdfDate2); System.out.println("sdfDate2 millis: " + sdfDate2.getTime()); } } And here is problem: OUTPUT: parsedCalendar: -62135622000000 parsedCalendar TZ: sun.util.calendar.ZoneInfo[id="Asia/Novosibirsk",offset=25200000,dstSavings=0,useDaylight=false,transitions=67,lastRule=null] parsedCalendar Date: Mon Jan 03 00:00:00 NOVT 1 sdfDate: Sat Jan 01 00:00:00 NOVT 1 sdfDate millis: -62135794800000 parsedCalendar2: 320691600000 parsedCalendar2 TZ: sun.util.calendar.ZoneInfo[id="Asia/Novosibirsk",offset=25200000,dstSavings=0,useDaylight=false,transitions=67,lastRule=null] parsedCalendar2 Date: Sat Mar 01 00:00:00 NOVT 1980 sdfDate2: Sat Mar 01 00:00:00 NOVT 1980 sdfDate2 millis: 320691600000 DEBUG: parsedCalendar.getTimeInMillis() = -62135622000000 sdfDate.getTime() = -62135794800000 parsedCalendar.getTime() = {Date#790} "Mon Jan 03 00:00:00 NOVT 1" sdfDate = {Date#759} "Sat Jan 01 00:00:00 NOVT 1" parsedCalendar2.getTimeInMillis() = 320691600000 sdfDate2.getTime() = 320691600000 parsedCalendar2.getTimeZone() = {ZoneInfo#755} "sun.util.calendar.ZoneInfo[id="Asia/Novosibirsk",offset=25200000,dstSavings=0,useDaylight=false,transitions=67,lastRule=null]" parsedCalendar.getTimeZone() = {ZoneInfo#756} "sun.util.calendar.ZoneInfo[id="Asia/Novosibirsk",offset=25200000,dstSavings=0,useDaylight=false,transitions=67,lastRule=null]" simpleDateFormat.getTimeZone() = {ZoneInfo#757} "sun.util.calendar.ZoneInfo[id="Asia/Novosibirsk",offset=25200000,dstSavings=0,useDaylight=false,transitions=67,lastRule=null]" simpleDateFormat2.getTimeZone() = {ZoneInfo#758} "sun.util.calendar.ZoneInfo[id="Asia/Novosibirsk",offset=25200000,dstSavings=0,useDaylight=false,transitions=67,lastRule=null]" As you can see in parsing 0001 dateTime there is difference in ms! And in parsing 1980 it is not. Who can explain why?
This is due to the difference between the Julian and Gregorian calendar systems. SimpleDateFormat uses the default calendar system, which I believe to be GregorianCalendar on both your system and mine. GregorianCalendar (despite its name) switches between the Gregorian calendar system and the Julian calendar system, based on the gregorianChange property. It assumes that any date provided after that cut-over is Gregorian, and any before it is Julian. The default cut-over is in 1582. DatatypeConverter uses a pure proleptic Gregorian calendar instead, as that's what the W3C XML Schema docs require. That means if you parse a value just before the calendar switch, you'll see a large difference - and that difference will get smaller as you go further back in time, with a difference of 3 days every 400 years. (The three century-years which aren't divisible by 400, and so are leap years in the Julian calendar but not in the Gregorian calendar.) If you set the calendar in the SimpleDateFormat to a GregorianCalendar which you've called setGregorianChange(Long.MIN_VALUE) on first, the two will agree. Here's code to make it easier to explore the difference: import javax.xml.bind.DatatypeConverter; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.concurrent.TimeUnit; public class Test { public static void main(String[] args) throws ParseException { convert("0001-01-01T00:00:00"); convert("1000-01-01T00:00:00"); convert("1580-01-01T00:00:00"); convert("1590-01-01T00:00:00"); convert("1980-03-01T00:00:00"); } private static void convert(String input) throws ParseException { Calendar datatypeConverterResult = DatatypeConverter.parseDateTime(input); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); Date sdfResult = simpleDateFormat.parse(input); System.out.println("Input: " + input); long datatypeConverterMillis = datatypeConverterResult.getTimeInMillis(); long sdfResultMillis = sdfResult.getTime(); long days = TimeUnit.MILLISECONDS.toDays(datatypeConverterMillis - sdfResultMillis); System.out.println("DatatypeConverter epoch millis: " + datatypeConverterMillis); System.out.println("SimpleDateTime epoch millis: " + sdfResultMillis); System.out.println("Difference in days: " + days); System.out.println("Parsed calendar time zone: " + datatypeConverterResult.getTimeZone().getID()); System.out.println(); } } Note that on Java 9, you need to specify the module explicitly. This is simplest done with java.se.ee: $ javac Test.java --add-modules java.se.ee $ java --add-modules java.se.ee Test Output on my box: Input: 0001-01-01T00:00:00 DatatypeConverter epoch millis: -62135596800000 SimpleDateTime epoch millis: -62135769600000 Difference in days: 2 Parsed calendar time zone: Europe/London Input: 1000-01-01T00:00:00 DatatypeConverter epoch millis: -30610224000000 SimpleDateTime epoch millis: -30609792000000 Difference in days: -5 Parsed calendar time zone: Europe/London Input: 1580-01-01T00:00:00 DatatypeConverter epoch millis: -12307248000000 SimpleDateTime epoch millis: -12306384000000 Difference in days: -10 Parsed calendar time zone: Europe/London Input: 1590-01-01T00:00:00 DatatypeConverter epoch millis: -11991628800000 SimpleDateTime epoch millis: -11991628800000 Difference in days: 0 Parsed calendar time zone: Europe/London Input: 1980-03-01T00:00:00 DatatypeConverter epoch millis: 320716800000 SimpleDateTime epoch millis: 320716800000 Difference in days: 0 Parsed calendar time zone: Europe/London
Java: Date/Time from Database to milliseconds, hour disappearing
I am having an issue with Dates and timezones. I have a MySQL InnoDB database which holds two fields DATE(yyyy-MM-dd) and TIME(HH:mm:ss). These are known as to be UTC (0 GMT). My computer is based in CET (+1 GMT). • dateObject is the result from this resultSet.getTime("date_field") (java.sql.Date) • timeObject is a result from this resultSet.getDate("time_field") (java.sql.Time) In the database DATE is stored as 2014-02-22 and TIME 15:00 System.out.println("Untouched "+dateObject+" "+timeObject); long date = dateObject.getTime(); long time = timeObject.getTime(); System.out.println("Touched "+new Date(date+time)); Results in the following output: Untouched 2014-02-22 15:00:00 Touched Sat Feb 22 14:00:00 CET 2014 Why is one hour being skipped off the Touched output? I was expecting the following: Untouched 2014-02-22 15:00:00 Touched Sat Feb 22 15:00:00 CET 2014 To rumble things up I have tried with the following also: SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println(sdf.format(new Date(date+time))); And result: 2014-02-22 14:00:00 All in all. I am expecting GMT+1 to show 16:00(local) and GMT+0 to display 15:00
I think I did answer ma question (Remember timeObject in the db is 15:00:00 at UTC): TimeZone tz = TimeZone.getTimeZone("Gmt0"); SimpleDateFormat sdfFull = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); sdfFull.setTimeZone(tz); Date updateDate = sdfFull.parse(dateObject.toString()+" "+timeObject.toString()); System.out.println(updateDate); Results in what I was hoping for: Sat Feb 22 16:00:00 CET 2014
The reason is similar to this SO-answer. But note following details about toString(). java.util.Date.toString() => output dependent on your system time zone in format pattern "EEE MMM dd HH:mm:ss zzz yyyy" java.sql.Date.toString() => output in format "yyyy-MM-dd" (your dateObject) java.sql.Time.toString() => output in format "HH:mm:ss" (your timeObject) The sql representations are not dependent on time zone. So you compare apples and peaches. Supplementary remark: I have invested more in testing and found: java.sql.Date dateObj = new java.sql.Date(2014 - 1900, Calendar.FEBRUARY, 22); Time timeObj = new Time(15, 0, 0); Time midnight = new Time(0, 0, 0); Date d = new Date(dateObj.getTime() + timeObj.getTime()); System.out.println("dateObj: " + dateObj + "/" + dateObj.getTime()); // dateObj: 2014-02-22/1393023600000, one hour less than a full day because of UTC-long System.out.println("timeObj: " + timeObj + "/" + timeObj.getTime()); // timeObj: 15:00:00/50400000 => one hour less as UTC-long System.out.println("midnight: " + midnight + "/" + midnight.getTime()); // midnight: 00:00:00/-3600000 => one hour less, negative! System.out.println(new Date(dateObj.getTime())); // Sat Feb 22 00:00:00 CET 2014 System.out.println(new Date(timeObj.getTime())); // Thu Jan 01 15:00:00 CET 1970 System.out.println(d); // Sat Feb 22 14:00:00 CET 2014 So I strongly suspect following effect: Both dateObject and timeObject are been calculated your system time zone, therefore their utc-long values show both one hour less - the time zone offset. If you combine both in one Date-object by just summarizing up then one of both deltas gets lost because one single date object cannot take in account two offsets. Conclusion: You tried to combine date and time by summarize their utc-longs, but this is in general a faulty approach. Date plus Date is not Date, but undefined! In domain-specific language you can only add a duration/period to a date/time. So a solution having a midnight object could be: Date d = new Date(dateObj.getTime() + timeObj.getTime() - midnight.getTime()); System.out.println(d); // Sat Feb 22 15:00:00 CET 2014, correct - what you wanted
wrong time calculating in Java
when I want to sum two dates in java it does not work: System.out.println(date + " <---- date"); System.out.println(time + " <---- time"); System.out.println(new Date(date.getTime() + time.getTime()) + " <---- new Date(time.getTime() + date.getTime())"); leads to following output: Wed Nov 06 00:00:00 CET 2013 <---- date Thu Jan 01 11:51:14 CET 1970 <---- time Wed Nov 06 10:51:14 CET 2013 <---- new Date(time.getTime() + date.getTime()) ... but if i work with Calender it works! Calendar calendar = Calendar.getInstance(); calendar.setTime(time); int hour = calendar.get(Calendar.HOUR_OF_DAY); int min = calendar.get(Calendar.MINUTE); calendar.setTime(date); calendar.set(Calendar.HOUR_OF_DAY, hour); calendar.set(Calendar.MINUTE, min); calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.MILLISECOND, 0); Date myDate = calendar.getTime(); System.out.println(myDate); results in Wed Nov 06 11:51:00 CET 2013 which is correct Can anybody explain me why?
Fundamentally, you've got problems with time zones here. The fact that you're using a java.util.Date to represent a time of day is messing you up to start with. Your time of 11:51:14 CET is actually 10:51:14 UTC, so when you add the result of calling time.getTime(), you're only adding "just under 11 hours" rather than "just under 12 hours". The use of inappropriate data types makes all this hard to work with and understand. I'd strongly recommend using Joda Time for all of this. Then you can start with a LocalDate and LocalTime, combine them into a LocalDateTime and then work out if you want to apply a particular time zone. Using the right data types, which mean exactly what you're trying to convey, makes a huge difference for date/time work.
Java Date year calculation is off by year for two days
This caused a Y2K-style bug in my software if you can imagine. Strange thing is the off-by-one year calculation only occurs for two days in the year, which I'm less sure how to troubleshoot. The output: 03-Jan-2013 02-Jan-2013 01-Jan-2013 31-Dec-2013 ** strange 30-Dec-2013 ** strange 29-Dec-2012 28-Dec-2012 27-Dec-2012 26-Dec-2012 25-Dec-2012 I am not sure which part of the Java date utilities could cause such an error. The code (since the test is so small I included a complete working program): import java.util.Calendar; import java.util.Date; import java.text.SimpleDateFormat; public class DateT { private static String getFormattedBackscanStartTime(int days) { SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MMM-YYYY"); Calendar workingDate = Calendar.getInstance(); workingDate.add(Calendar.DATE, -1 * days); String formattedStartTime = dateFormat.format(workingDate.getTime()); return formattedStartTime; } public static void main(String args[]) { for(int i = 35; i < 45; i++) { System.out.println(getFormattedBackscanStartTime(i)); } } }
This is the problem: "dd-MMM-YYYY" YYYY is the week-year, not the calendar year. You want yyyy instead. The last two days of calendar year 2012 were in the first week of week-year 2013. You should normally only use the week year in conjunction with the "week of year" specifier (w).
I am assuming you are using java 1.7. The code snippet above will not work with java 1.6 as SimpleDateFormat("dd-MMM-YYYY") will raise an java.lang.IllegalArgumentException (YYYY is not available in java 1.6) You need to use yyyy instead of YYYY. Y -> week-year y -> year here EDIT Works great with yyyy: $ java DateT 03-Jan-2013 02-Jan-2013 01-Jan-2013 31-Dec-2012 30-Dec-2012 29-Dec-2012 28-Dec-2012 27-Dec-2012 26-Dec-2012 25-Dec-2012
The problem lies in your date format string - year should be yyyy not YYYY. If you print the value of workingDate.getTime() in each iteration of the loop, you'll see it has the expected values: Thu Jan 03 11:19:33 EST 2013 Wed Jan 02 11:19:33 EST 2013 Tue Jan 01 11:19:33 EST 2013 Mon Dec 31 11:19:33 EST 2012 Sun Dec 30 11:19:33 EST 2012 Sat Dec 29 11:19:33 EST 2012 Fri Dec 28 11:19:33 EST 2012 Thu Dec 27 11:19:33 EST 2012 Wed Dec 26 11:19:33 EST 2012 Tue Dec 25 11:19:33 EST 2012 Therefore the problem lies in the SimpleDateFormat usage.
For the sake of completeness, here’s the modern answer using LocalDate (as recommended by Basil Bourque in a comment). import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.Locale; public class DateT { private static DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd-MMM-uuuu", Locale.US); private static String getFormattedBackscanStartTime(int days) { return LocalDate.now(ZoneId.systemDefault()).minusDays(days).format(dateFormatter); } public static void main(String args[]) { for(int i = 155; i < 165; i++) { System.out.println(getFormattedBackscanStartTime(i)); } } } Running this today I got 04-Jan-2017 03-Jan-2017 02-Jan-2017 01-Jan-2017 31-Dec-2016 30-Dec-2016 29-Dec-2016 28-Dec-2016 27-Dec-2016 26-Dec-2016 A few things to note: Give an explicit locale to your formatter to control the langauge of your output. Even if you just pass Locale.getDefault() you are telling the reader that you have thought about locale and made a decision. Similarly give an explicit time zone to LocalDate.now() to tell the reader you’ve made a decision (for example ZoneId.of("America/New_York") for a specific time zone; ZoneId.systemDefault() for the JVM’s current time zone setting). I find the code simpler and more straightforward than the code using the oldfashioned Calendar class. This is typical for the newer classes. I have used uuuu for year. yyyy (lowercase) works too, there will only be a difference for years before the common era (AKA BC).
You need to use lower case y for the year. Try this: SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MMM-yyyy");