Calendar`s getActualMinimum returns wrong value - java

I need to compare a certain date to the current date/time to make sure that it comes before the first day/hour/min/sec of the current month. In order to implement this feature, a configure a Calendar instance using its getActualMinimum method, however, today (Thursday, 19/01/2023 - 10:40:18 BRT 2023), it presented a behavior that I have never faced before. Consider the following code:
Calendar cal = Calendar.getInstance();
System.out.println("After instantiation: " + cal.getTime());
cal.set(Calendar.DAY_OF_MONTH, cal.getActualMinimum(Calendar.DAY_OF_MONTH));
System.out.println("After configuring the Day of Month: " + cal.getTime());
cal.set(Calendar.HOUR_OF_DAY, cal.getActualMinimum(Calendar.HOUR_OF_DAY));
System.out.println("After configuring the Hour of day: " + cal.getTime());
cal.set(Calendar.MINUTE, cal.getActualMinimum(Calendar.MINUTE));
System.out.println("After configuring the Minutes: " + cal.getTime());
cal.set(Calendar.SECOND, cal.getActualMinimum(Calendar.SECOND));
System.out.println("After configuring the Seconds: " + cal.getTime());
cal.set(Calendar.MILLISECOND, cal.getActualMinimum(Calendar.MILLISECOND));
System.out.println("After configuring the Millis: " + cal.getTime());
The code above, in the moment that this post is being created, would print to the console:
After instantiation: Thu Jan 19 10:40:18 BRT 2023
After configuring the Day of Month: Sun Jan 01 10:40:18 BRT 2023
After configuring the Hour of day: Sat Dec 31 23:40:18 BRT 2022
After configuring the Minutes: Sat Dec 31 23:00:18 BRT 2022
After configuring the Seconds: Sat Dec 31 23:00:00 BRT 2022
After configuring the Millis: Sat Dec 31 23:00:00 BRT 2022
Could someone explain why, after configuring the Hour of day, the value was set to 23 and not 00?
Edit: I`m using Java 8, specifically JDK 1.8.0_241
My current and default Time Zone is the Horário Padrão de Brasília (BRT or GMT-3)

java.time
The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API.
With java.time, the modern date-time API, you have specialized types for different purposes. A very common type is ZonedDateTime which contains the information of the timezone along with the date and time information.
Note: Unlike java.util date-time types, java.time types are immutable i.e. you always get a new instance on setting a new value; therefore, like a String, you need to assign the new value to the reference if you want the reference to point to the new value.
Demo:
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAdjusters;
import java.util.Locale;
class Main {
public static void main(String[] args) {
// Replace ZoneId.systemDefault() with the applicable ZoneId e.g.
// ZoneId.of("America/New_York")
ZonedDateTime zdt = ZonedDateTime.now(ZoneId.systemDefault());
System.out.println(zdt);
zdt = zdt.with(TemporalAdjusters.firstDayOfMonth());
System.out.println(zdt);
zdt = zdt.withHour(LocalTime.MIN.getHour());
System.out.println(zdt);
zdt = zdt.withMinute(LocalTime.MIN.getMinute());
System.out.println(zdt);
zdt = zdt.withSecond(LocalTime.MIN.getSecond());
System.out.println(zdt);
zdt = zdt.with(ChronoField.MILLI_OF_SECOND, LocalTime.MIN.getLong(ChronoField.MILLI_OF_SECOND));
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSXXX", Locale.ENGLISH);
System.out.println(zdt.format(formatter));
// In a single statement
String output = ZonedDateTime.now(ZoneId.systemDefault())
.with(TemporalAdjusters.firstDayOfMonth())
.withHour(LocalTime.MIN.getHour())
.withMinute(LocalTime.MIN.getMinute())
.withSecond(LocalTime.MIN.getSecond())
.with(ChronoField.MILLI_OF_SECOND, LocalTime.MIN.getLong(ChronoField.MILLI_OF_SECOND))
.format(formatter);
System.out.println(output);
// There is a better way if all you want is day-1 with minimum time
zdt = LocalDate.now(ZoneId.systemDefault())
.with(TemporalAdjusters.firstDayOfMonth())
.atStartOfDay()
.atZone(ZoneId.systemDefault());
System.out.println(zdt.format(formatter));
}
}
Output from a sample run:
2023-01-19T16:50:43.811714Z[GMT]
2023-01-01T16:50:43.811714Z[GMT]
2023-01-01T00:50:43.811714Z[GMT]
2023-01-01T00:00:43.811714Z[GMT]
2023-01-01T00:00:00.811714Z[GMT]
2023-01-01T00:00:00.000Z
2023-01-01T00:00:00.000Z
2023-01-01T00:00:00.000Z
ONLINE DEMO
Learn more about the modern Date-Time API from Trail: Date Time.
In case you need a solution using the legacy API:
Calendar#getTime returns an instance of java.util.Date which is not a real date-time object; rather, it just contains the number of milliseconds from January 1, 1970, 00:00:00 GMT. The Date#toString applies the system's timezone to calculate the date-time and returns the same.
The way to get the date-time string with the desired timezone is by applying the timezone to the SimpleDateFormat and using it to format the instance of java.util.Date.
Demo:
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;
class Main {
public static void main(String[] args) {
SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss.SSS z yyyy", Locale.ENGLISH);
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
Calendar cal = Calendar.getInstance();
System.out.println("After instantiation: " + sdf.format(cal.getTime()));
cal.set(Calendar.DAY_OF_MONTH, cal.getActualMinimum(Calendar.DAY_OF_MONTH));
System.out.println("After configuring the Day of Month: " + sdf.format(cal.getTime()));
cal.set(Calendar.HOUR_OF_DAY, cal.getActualMinimum(Calendar.HOUR_OF_DAY));
System.out.println("After configuring the Hour of day: " + sdf.format(cal.getTime()));
cal.set(Calendar.MINUTE, cal.getActualMinimum(Calendar.MINUTE));
System.out.println("After configuring the Minutes: " + sdf.format(cal.getTime()));
cal.set(Calendar.SECOND, cal.getActualMinimum(Calendar.SECOND));
System.out.println("After configuring the Seconds: " + sdf.format(cal.getTime()));
cal.set(Calendar.MILLISECOND, cal.getActualMinimum(Calendar.MILLISECOND));
System.out.println("After configuring the Millis: " + sdf.format(cal.getTime()));
}
}
Output from a sample run:
After instantiation: Thu Jan 19 15:29:38.381 UTC 2023
After configuring the Day of Month: Sun Jan 01 15:29:38.381 UTC 2023
After configuring the Hour of day: Sun Jan 01 00:29:38.381 UTC 2023
After configuring the Minutes: Sun Jan 01 00:00:38.381 UTC 2023
After configuring the Seconds: Sun Jan 01 00:00:00.381 UTC 2023
After configuring the Millis: Sun Jan 01 00:00:00.000 UTC 2023
ONLINE DEMO

Probably this is caused by the default TimeZone of your PC , try adding this after you declare the Calnedar object
cal.setTimeZone(TimeZone.getTimeZone("UTC"));

Related

Date is displaying in different time zone when parsing a string using SimpleDateFormat [duplicate]

This question already has answers here:
Java date parsing has stranger behavior where parsing different date in different timezone
(1 answer)
Java Date timezone printing different timezones for different years, Workaround needed
(4 answers)
Closed 3 months ago.
public class ParseDate {
public static final String DATE_FORMAT = "yyyy-MM-dd";
public static SimpleDateFormat DateFormatter = new SimpleDateFormat(DATE_FORMAT);
public static void main(String[] args) {
try {
System.out.println("Converting 2020-12-31 to "+DateFormatter.parse("2020-12-31"));
System.out.println("Converting 2020-06-30 to "+DateFormatter.parse("2020-06-30"));
} catch (ParseException e) {
e.printStackTrace();
}
}
}
Output:
Converting 2020-12-31 to Thu Dec 31 00:00:00 GMT 2020
Converting 2020-06-30 to Tue Jun 30 00:00:00 BST 2020
If I execute this code, I am getting different time zones (GMT and BST) as output.
How to get same time zone of Date object as output irrespective of different input strings.
When I was executed the code, I expect the date object should contain same time zone (either GMT or BST).
I have tried with the below time zone:
java.util.Date is not a true date-time object; rather, it just represents the number of milliseconds from January 1, 1970, 00:00:00 GMT. The Date#toString returns this millisecond value into a string applying the default timezone which is Europe/London in your case and therefore it prints GMT and BST because of DST.
java.time
The java.time API, released with Java-8 in March 2014, supplanted the error-prone legacy date-time API. Since then, using this modern date-time API has been strongly recommended.
Demo using modern date-time API
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
ZoneId zoneId = ZoneId.of("Europe/London");
ZonedDateTime zdt1 = LocalDate.parse("2020-12-31").atStartOfDay(zoneId);
ZonedDateTime zdt2 = LocalDate.parse("2020-06-30").atStartOfDay(zoneId);
System.out.println("Converting 2020-12-31 to " + zdt1);
System.out.println("Converting 2020-06-30 to " + zdt2);
// Date#toString like format
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss z uuuu", Locale.ENGLISH);
System.out.println(zdt1.format(formatter));
System.out.println(zdt2.format(formatter));
}
}
Output:
Converting 2020-12-31 to 2020-12-31T00:00Z[Europe/London]
Converting 2020-06-30 to 2020-06-30T00:00+01:00[Europe/London]
Thu Dec 31 00:00:00 GMT 2020
Tue Jun 30 00:00:00 BST 2020
Note that since java.time API is based on ISO 8601 standard, you do not need to specify a parser ( DateTimeFormatter in the case of java.time API) to parse a date string which is already in this format.
Learn more about the modern Date-Time API from Trail: Date Time.

java.util.Date special date 1980-04-06: wrong hour

We faced following problem:
SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy", java.util.Locale.GERMAN);
String dateInString = "06-04-1980";
Date date = formatter.parse(dateInString);
before: Sun Apr 06 00:00:00 CEST 1980
after: Sun Apr 06 01:00:00 CEST 1980
Another example:
Date date = Date.from(LocalDate.of(1980, 4, 6).atStartOfDay(ZoneId.systemDefault()).toInstant());
Facing the same problem.
We thought change of java11 to java17 is the problem, but it wasn't. It was the change of the timezone from Europe/Berlin to Europe Vienna. On 1980-04-06 the daylight saving time was established in Austria and the change of the hour was at 00:00. So there was no 00:00 at this date.
Reproduceable example - changing timezone to "Europe/Berlin" results in 0 hour.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy");
for (LocalDate date = LocalDate.of(1500, 04, 01); date.isBefore(LocalDate.of(9999, 1, 1)); date = date.plusDays(1)) {
Date out = Date.from(date.atStartOfDay(ZoneId.of("Europe/Vienna")).toInstant());
Calendar cal = Calendar.getInstance();
cal.setTimeZone(TimeZone.getTimeZone(ZoneId.of("Europe/Vienna")));
cal.setTime(out);
if (cal.get(Calendar.HOUR_OF_DAY) > 0) {
System.out.println(date.format(formatter) + " > " + cal.get(Calendar.HOUR_OF_DAY));
}
}
System.out.println("done");
All dates before 1893-03-31 have 23 as hour in timezone "Europe/Vienna", in "Europe/Berlin" its also 0.
It's not really a problem, it's a special thing about timezone "Europe/Vienna" which was changed in our system. If you get this problem, check your timezone, maybe it was changed by some other properties.
While summer time (DST) started at 02:00 o’clock in Germany on that date, it started already at midnight in Austria (Europe/Vienna time zone), so the time 00:00 did not exist, which is why we suddenly got 01:00. Both time zones are printed as CEST (Central European Summer Time), so the abbreviation does not allow us to distinguish.
I am in Israel, so in order to simulate the time zone of central Europe (using Java 17), I set the [System] property user.timezone to CET using the -D option of the java command. Then I ran the following code:
/*
* import java.time.ZoneId;
* import java.time.ZonedDateTime;
* import java.util.Date;
*/
ZonedDateTime zdt = ZonedDateTime.of(1980, 4, 6, 0, 0, 0, 0, ZoneId.systemDefault());
System.out.println(Date.from(zdt.toInstant()));
The result I got was:
Sun Apr 06 00:00:00 CET 1980
This is the result that you want, correct?

How to get selected month last date last timestamp in java?

From the below java code I'm getting a month First & last dateTimestamp, but here i need last dateTimestamp as - "Mon Aug 31 23:59:59 IST 2015" instead of - "Mon Aug 31 00:00:00 IST 2015"?
Calendar cal = Calendar.getInstance();
int year = 2015;
int month = 07;
cal.set(cal.DATE,1);
cal.set(cal.YEAR,year);
cal.set(cal.MONTH, month);
String firstDate = (cal.getActualMinimum(cal.DATE) + "/" + (month+1) + "/" +year);
System.out.println("firstDate-->"+"\t"+firstDate);
String lastDate = (cal.getActualMaximum(cal.DATE) + "/" + (month+1) + "/" +year);
System.out.println("lastDate-->"+"\t"+lastDate);
DateFormat firstFormat = new SimpleDateFormat("dd/MM/yyyy");
Date beginDate = firstFormat.parse(firstDate);
System.out.println("BeginDate Timestamp"+ "\t" + beginDate);
DateFormat secoundFormat = new SimpleDateFormat("dd/MM/yyyy");
Date endDate = secoundFormat.parse(lastDate);
System.out.println("endDate Timestamp"+ "\t" + endDate);
Output:->
firstDate--> 1/8/2015
lastDate--> 31/8/2015
BeginDate Timestamp Sat Aug 01 00:00:00 IST 2015
endDate Timestamp Mon Aug 31 00:00:00 IST 2015
Please help me if we have any solution.
If I understand your question, it looks as if you want to pass a year and month into a method, and get back the last day of the passed month.
I would suggest consider (in this order):
which jdk you use
configuration of calendar
configuration of timezone (maybe)
using jodatime
As of 1.8 many JodaTime-like features have been added to the jdk- e.g. see https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html (If you arent using 1.8 you can use the joda lib, assuming your organization allows it)
Calendar.getInstance() gives a default TimeZone and a default Locale, which means the definitions of the running JVM. You may or may not need to consider this and implement more than just getInstance(). See API javadoc.
Assuming a Gregeorian Calendar (hey, you could be Bahaian and have 19 months in the year ...) , here is one partial implementation using JDK 1.7, JodaTime jar 2.2, validation-1.0.0.GA jar:
#Test
public void testDate() {
final String dateStringOfFirstDay = "1.7.2015";
final Date lastDayOfMonth = getLastDayOfMonth(dateStringOfFirstDay);
Assert.assertNotNull(lastDayOfMonth);
//more assertions ...
}
private Date getLastDayOfMonth(#NotNull String dateStringOfFirstDay) {
//further validation here necessary according to required date format
DateTime dt = DateTime.parse(dateStringOfFirstDay);
return dt.dayOfMonth().withMaximumValue().toDate();
}
The defintion of DateFormat/DateTimeFormat and further result assertions is left up to you.
Best of luck!
Guys I got a solution for my question!!!
I think it will help u too...
Calendar calendar = Calendar.getInstance();
int year=2015;
int month=7;
int date=31;
int hourOfDay=23;
int minute=59;
int second=59;
int milliSecond=999;
calendar.set(year, month, date, hourOfDay, minute, second);
calendar.set(calendar.MILLISECOND, milliSecond);
System.out.println("Time from Calendar: "+calendar.getTime());
long timeInMilliSeconds=calendar.getTimeInMillis();
System.out.println("timeInMilliSeconds from calendar: "+timeInMilliSeconds);
Timestamp timestamp=new Timestamp(timeInMilliSeconds);
System.out.println(timestamp);
The above program gives the last date last timestamp in a selected month.
getTimeInMillis() takes the time from Jan 01, 1970 to current time in Milliseconds.
Using those milliseconds i'm getting the Timestamp.
Thank you for your help guys!!!
OutPut:->
Time from Calendar: Mon Aug 31 23:59:59 IST 2015
timeInMilliSeconds from calendar: 1441045799999
2015-08-31 23:59:59.999

Java time formatting

I have the method
public static void testDateFormat() throws ParseException {
DateFormat dateFormat=new SimpleDateFormat("HH:mm:ss");
Date hora;
hora=dateFormat.parse("00:00:01");
System.out.println(hora.getHours()+" "+hora.getMinutes());
System.out.println("Date "+hora);
System.out.println("Seconds "+TimeUnit.MILLISECONDS.toSeconds(hora.getTime()));
}
The output is
0 0
Date Thu Jan 01 00:00:01 COT 1970
Seconds 18001
Why the number of seconds is 18001? I expected to get 1 second.
Because your Date has a TimeZone that is not UTC. It is, in fact, COT - which is UTC-5. And 5*60*60 is 18000 (or your result, plus one second). To get the value you expect, you could call DateFormat#setTimeZone(TimeZone) like,
DateFormat dateFormat=new SimpleDateFormat("HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); // <-- Add this.
Date hora=dateFormat.parse("00:00:01");
System.out.println(hora.getHours()+" "+hora.getMinutes());
System.out.println("Date "+hora);
System.out.println("Seconds "+TimeUnit.MILLISECONDS.toSeconds(hora.getTime()));
Output is as you expect.
Edit
As noted in the comments, Date#getTime() per the Javadoc
Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT represented by this Date object.
And your Date of
Thu Jan 01 00:00:01 COT 1970
is equivalent to
Thu Jan 01 00:05:01 UTC 1970
and thus you get the 5 hour difference.
The answer by Elliott Frisch is correct.
Time-Only
But if you are working with time-only without date or time zone, then use a date-time library that can handle that explicitly rather than hacking the java.util.Date class.
LocalTime
Use either the Joda-Time library or the java.time package in Java 8. Both offer a LocalTime class.
LocalTime localTime = LocalTime.parse( "00:00:01" );
int minuteOfHour = localTime.getMinuteOfHour();

Input date Jan 03 but returns Jan 02, why?

public static void main(String[] args) {
String opDate = "Tue Jan 03 00:00:00 MSK 2006";
String date = convertDate(opDate, "yyyyMMdd");
System.out.println("opDate: " + opDate);
System.out.println("date: " + date);
}
public static String convertDate(String opDate, String dateFormat) {
Date date = new Date();
// Mon Jan 02 00:00:00 MSK 2006
SimpleDateFormat dateParser = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US);
try {
date = dateParser.parse(opDate);
} catch (Exception e) {
System.out.println("exception = " + e.toString());
}
SimpleDateFormat df = new SimpleDateFormat(dateFormat);
df.setTimeZone(TimeZone.getTimeZone("Russia/Moscow"));
String strDate = df.format( date.getTime() );
return strDate.trim();
}
out:
opDate: Tue Jan 03 00:00:00 MSK 2006
date: 20060102
Why does it return Jan 02?
The problem is the fetching of the "Russia/Moscow" time zone. The correct zoneinfo ID is "Europe/Moscow". Change the ID, and the problem goes away:
df.setTimeZone(TimeZone.getTimeZone("Europe/Moscow"));
It's unfortunate that TimeZone.getTimeZone("random rubbish") returns the UTC time zone rather than letting you know in some way that it's broken.
Probably due to timezone conversion.
I would suggest you also print the time and the timezone of your resulting date. This is likely to be Jan 2, 23:00 or something.
This happens because you set a different timezone on the SimpleDataFormat.
Timezones.
You're specifying midnight on January 3rd in MSK. This is 9pm on the 2nd January in GMT (the likely default timezone).
I can see that you're trying to output in Moscow time as well, but Russia/Moscow is not a valid timezone, and the getTimeZone call "helpfully" silently defaults to returning GMT. This then of course doesn't change the time zone of the date when formatting and outputs it as 2 Jan.
If you set the timezone to Europe/Moscow, you'll get the expected output.
May it be related to the fact that you're converting dates between two distinct TimeZones?
If you change line:
String date = convertDate(opDate, "yyyyMMdd");
to:
String date = convertDate(opDate, "EEE MMM dd HH:mm:ss zzz yyyy");
you can see the output of your program:
opDate: Tue Jan 03 00:00:00 MSK 2006
date: Mon Jan 02 20:00:00 GMT 2006
You are not setting well TimeZone with:
df.setTimeZone(TimeZone.getTimeZone("Russia/Moscow"));
you need:
df.setTimeZone(TimeZone.getTimeZone("Europe/Moscow"));
Finally there are summer delay of 1h.
java.time
The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*.
Solution using java.time, the modern Date-Time API:
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
String strDateTime = "Tue Jan 03 00:00:00 MSK 2006";
String formatted = formatDateTimeStringTo(strDateTime, "yyyyMMdd", Locale.ENGLISH);
System.out.println("opDate: " + strDateTime);
System.out.println("date: " + formatted);
}
public static String formatDateTimeStringTo(String strDateTime, String targetFormat, Locale locale) {
DateTimeFormatter parser = DateTimeFormatter.ofPattern("EEE MMM d H:m:s zzz u", locale);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(targetFormat, locale);
ZonedDateTime zdt = ZonedDateTime.parse(strDateTime, parser);
// System.out.println(zdt); // 2006-01-03T00:00+03:00[Europe/Moscow]
return zdt.format(formatter);
}
}
Output:
opDate: Tue Jan 03 00:00:00 MSK 2006
date: 20060103
ONLINE DEMO
Learn more about the modern Date-Time API from Trail: Date Time.
Follow the standard convention to name the timezone:
Ask your publisher to switch to the standard naming convention of the timezone. The standard naming convention is Region/City e.g. Europe/Moscow. The two/three/four letter abbreviation for the timezone is error-prone as described in the following text at the Timezone documentation page:
Three-letter time zone IDs
For compatibility with JDK 1.1.x, some other three-letter time zone
IDs (such as "PST", "CTT", "AST") are also supported. However, their
use is deprecated because the same abbreviation is often used for
multiple time zones (for example, "CST" could be U.S. "Central
Standard Time" and "China Standard Time"), and the Java platform can
then only recognize one of them.
* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.

Categories