I heard that Unix time does not include "Leap Second". And I also heard that Java Calendar API does not include Leap second.
Since 1972, 27 seconds were added as the Leap second. And Unix time began 1970-01-01 00:00:00 (UTC).
So, I thought that there are 27 seconds difference between current UTC time and Unix time.
To clarify my thought, I did some experiment like below. 1614766198 was a Unix time at 2021-03-03 10:10:00 (UTC+0)
import java.util.Calendar;
import java.util.TimeZone;
public class CanendarTest {
public static void main(String[] args) throws InterruptedException {
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.setTimeInMillis(1614766198L * 1000);
System.out.println(cal.get(Calendar.YEAR));
System.out.println(cal.get(Calendar.MONTH));
System.out.println(cal.get(Calendar.DAY_OF_MONTH));
System.out.println(cal.get(Calendar.HOUR_OF_DAY));
System.out.println(cal.get(Calendar.MINUTE));
System.out.println(cal.get(Calendar.SECOND));
}
}
The result of above code was
output
2021
2
3
10
9
58
Output seems like "2021-03-03 10:09:58".
So, My Question is that, Why Java Calendar API return 2 second difference from 1970-01-01 00:00:00 (UTC) not 27 second difference?
1614766198 was a Unix time at 2021-03-03 10:10:00 (UTC+0)
This is not correct. The following UNIX command
TZ=UTC date -r 1614766198
outputs
Wed 3 Mar 2021 10:09:58 UTC
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.Instant;
public class Main {
public static void main(String[] args) {
Instant instant = Instant.ofEpochSecond(1614766198);
System.out.println(instant);
}
}
Output:
2021-03-03T10:09:58Z
ONLINE DEMO
An Instant represents an instantaneous point on the timeline in UTC. The Z in the output is the timezone designator for a zero-timezone offset. It stands for Zulu and specifies the Etc/UTC timezone (which has the timezone offset of +00:00 hours).
Learn more about the modern Date-Time API from Trail: Date Time.
* 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.
Related
Could some one explain why this past date getting increased by one hour , when I convert it to Moscow Timezone ?
I'm using JDK 1.6.0_12 version. .
2011-04-02T11:39:46+0300 --> Sat Apr 02 12:39:46 MSK 2011 // 11:39 --> 12:39
My current system time-zone is "Europe/Moscow" UTC+3 .
Also please note that this past date is in DST(Daylight Saving ) time-zone period UTC+4 , earlier used in Russia.
There was a legislative change of Russian time-zone definitions in October 2014 . Since then Russia uses UTC+3 all through out a year .
I already checked
this old post of 2014 . But I think this issue looks different.
Our developers expect that every past date (like "2011-04-02T11:39:46+0300" and which is in DST period ), should contain current time zone offset value i.e +0300 , not +0400 . And they think JRE is converting it incorrectly to UTC+4 , though "Default Time Zone Offset" shows +3 here . Is this way of handling time-zone offset value for past dates correct?
Same output is given on JRE 1.8 , which I think is an updated version ,there shouldn't be any issue in TZ definition in JRE 1.8.
Thanks in Advance !
Java Code:
import java.text.SimpleDateFormat;
import java.util.TimeZone;
import java.util.Date;
public class HelloWorld{
public static void main(String []args)
{
String dateInString = "2011-04-02T11:39:46+0300";
System.out.println(dateInString);
try {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
Date date = dateFormat.parse(dateInString);
System.out.println(date);
} catch (Exception e) {
System.out.println(e);
}
final TimeZone tzone = TimeZone.getDefault();
System.out.println("Default Time Zone ID - " + tzone.getID());
System.out.println("Default Time Zone Offset - (" + (tzone.getRawOffset() / 60 / 60 / 1000) + ") hour.");
}
}
Output :
2011-04-02T11:39:46+0300
Sat Apr 02 12:39:46 MSK 2011
Default Time Zone ID - Europe/Moscow
Default Time Zone Offset - (3) hour.
12:39 is the correct time
You are getting the correct result. In your string, 2011-04-02T11:39:46+0300, the trailing +0300 is an offset from UTC. So the point in time is the same as 2011-04-02T08:39:46+00:00 (UTC). As you say yourself, Moscow was at UTC offset +04:00 from 27 March 2011 to 26 October 2014. So to get the correct time for Moscow Java needs to add 1 hour to the hour in the string. Or 4 hours to the UTC hour of 08:39:46. In any case the time in Moscow was 12:39:46 at this point in time.
Or to answer your question:
… why this past date getting increased by one hour , when I convert it
to Moscow Timezone ?
Because Moscow on that date was 1 hour ahead of the time in the string.
java.time
That said I agree with those who recommend java.time, the modern Java date and time API, for the job. SimpleDateFormat is a notorious troublemaker of a class, and Date and TimeZone are poorly and confusingly designed too. All are long outdated. The modern API is so much nicer to work with.
For example:
ZoneId zone = ZoneId.of("Europe/Moscow");
ZonedDateTime zdt = ZonedDateTime.of(2011, 4, 2, 11, 39, 46, 0, zone);
System.out.println(zdt);
Output:
2011-04-02T11:39:46+04:00[Europe/Moscow]
You can also see from the output that Java knows that Moscow was at offset +04:00 back then.
Your question very well illustrates why java.time (opposite the old TimeZone class) makes the distinction between a time zone and an offset. A time zone includes all historic, the present and all known future offsets from UTC. This is what you need to represent historic times in Moscow correctly. In java.time a time zone is identified by a ZoneId object and obeys a ZoneRules object (most often we need not concern ourselves with the latter and can just trust Java to make the right conversions). A UTC offset is represented by a ZoneOffset object.
Question: how could I use java.time with Java 1.6?
This is your lucky day. java.time exactly requires at least Java 6.
In Java 8 and later and on newer Android devices (from API level 26) the modern API comes built-in.
In non-Android Java 6 and 7 get the ThreeTen Backport, the backport of the modern classes (ThreeTen for JSR 310; see the links at the bottom).
On older Android either use desugaring or the Android edition of ThreeTen Backport. It’s called ThreeTenABP. In the latter case make sure you import the date and time classes from org.threeten.bp with subpackages.
Links
Time Changes in Moscow Over the Years
Oracle tutorial: Date Time explaining how to use java.time.
Java Specification Request (JSR) 310, where java.time was first described.
ThreeTen Backport project, the backport of java.time to Java 6 and 7 (ThreeTen for JSR-310).
Java 8+ APIs available through desugaring
ThreeTenABP, Android edition of ThreeTen Backport
Question: How to use ThreeTenABP in Android Project, with a very thorough explanation.
Both modern java date/time api and legacy one (that is used in jdk1.6) rely on system unix time and the tzdata file bundled with the JRE. Looks like the developers are right and your java is using a very old one version of tzdata and your developers are right.
Also, the tzdata keeps information about legal changes and if you are trying to convert date/time in the past, it will apply conversion rules that were relevant at that time.
Regarding JDK 1.8: there was an update to Russian timezone information in 8u101, so you should use at least 8u101 for a better timezone conversion.
The best decision for you would be to use modern java or update your JREs tzdata manually if you really need to use an old one.
You need to set time-zone to SimpleDateFormat as shown below:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
public class Main {
public static void main(String[] args) throws ParseException {
String dateInString = "2011-04-02T11:39:46+0300";
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
dateFormat.setTimeZone(TimeZone.getTimeZone("Europe/Moscow"));// Set time-zone
Date date = dateFormat.parse(dateInString);
System.out.println(dateFormat.format(date));
}
}
Output:
2011-04-02T12:39:46+0400
Note that java.util.Date does not have time-zone information. It's simply the number of milliseconds from the standard Java epoch of 1970-01-01T00:00:00Z where Z stands for UTC (0 hour offset), also known as Zulu time-zone. At any given moment, you will get the same number of milliseconds on the JVMs sitting in any part of the word. When you try to print an object of java.util.Date, the date-time string for the JVM's time-zone is calculated from this milliseconds value and the same is displayed. If you want to get the date-time String in a specific time-zone, you need to set it explicitly to the SimpleDateFormat and use the same to format the java.util.Date.
I'm using the SimpleDateFormat object with the Date object as shown below. The problem lis that the Date object shows the wrong date, which is a few minutes off from the original string. The Date object appears to store the time in total milliseconds in the debugger.
Any ideas on the problem?
import java.text.SimpleDateFormat;
import java.util.Date;
Date played_at_local;
dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSSSSSZ");
played_at_local = dateFormat.parse("2011-04-11T22:27:18.491726-05:00");
//played_at_local shows "Mon Apr 11 22:35:29 America/Chicago 2011" in debugger
Try removing the fractional seconds from the format string. I just ran into the same issue, but with a slightly different format. My input format wasn't in ISO format (no "T", and no "Z"), but the symptom was the same -- time was off by some random number of minutes and seconds, but everything else was fine. This is what my log results looked like:
When using the fractional second format:
SimpleDateFormat dateFormater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS");
# Parsed date: 2011-05-27 17:11:15.271816 => Fri May 27 17:15:46 EDT 2011
# Parsed date: 2011-05-27 17:09:37.750343 => Fri May 27 17:22:07 EDT 2011
# Parsed date: 2011-05-27 17:05:55.182921 => Fri May 27 17:08:57 EDT 2011
# Parsed date: 2011-05-27 16:55:05.69092 => Fri May 27 16:56:14 EDT 2011
# Parsed date: 2011-05-27 16:38:35.50348 => Fri May 27 16:39:25 EDT 2011
I fixed it by removing the fractional seconds from the format.
SimpleDateFormat dateFormater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
# Parsed date: 2011-05-27 17:11:15.271816 => Fri May 27 17:11:15 EDT 2011
# Parsed date: 2011-05-27 17:09:37.750343 => Fri May 27 17:09:37 EDT 2011
# Parsed date: 2011-05-27 17:05:55.182921 => Fri May 27 17:05:55 EDT 2011
# Parsed date: 2011-05-27 16:55:05.69092 => Fri May 27 16:55:05 EDT 2011
# Parsed date: 2011-05-27 16:38:35.50348 => Fri May 27 16:38:35 EDT 2011
What I think is happening is that my "fractional seconds" part of the input string is too long (the same is true in the OP example). It appears to be expecting only three decimal places. If you do the math (take the first example):
fractional seconds = 0.271816 seconds
What DateFormat sees is 271816 / 1000 of a second
271816 / 1000 == 271 seconds
271 / 60 = 4 minutes
271 % 60 = 31 seconds
17:11:15 to 17:15:46 is exactly 4 minutes, 31 seconds off
Try this, working for me Z should be useed in date, or rmove from Format String
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSSSSS'Z'");
played_at_local = dateFormat.parse("2011-04-11T22:27:18.491726Z-05:00");
There are three major problems in your code:
You have used .SSSSSS for the fraction of a second whereas the SimpleDateFormat does not support a precision beyond milliseconds (.SSS). It also means that you need to limit the digits in the fraction of a second to three.
You have used Z to parse the timezone offset, -05:00 whereas the correct pattern for this is XXX.
You have used hh for a time in 24-Hour format whereas the correct pattern for this is HH. The symbol, hh is used for a time in 12-Hour (i.e. with am/pm) format.
Apart from this, I recommend you always use Locale with a date parsing/formatting API because parts of a date-time string are represented in different ways in different Locales.
Demo:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
public class Main {
public static void main(String[] args) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", Locale.ENGLISH);
Date date = sdf.parse("2011-04-11T22:27:18.491-05:00");
// Print the default string i.e. Date#toString
System.out.println(date);
// Print the date-time in a custom format
sdf.setTimeZone(TimeZone.getTimeZone("GMT-05:00"));
System.out.println(sdf.format(date));
}
}
Output:
Tue Apr 12 04:27:18 BST 2011
2011-04-11T22:27:18.491-05:00
Some facts about legacy date-time API:
The java.util.Date object is not a real date-time object like the modern date-time types; rather, it represents the number of milliseconds since the standard base time known as "the epoch", namely January 1, 1970, 00:00:00 GMT (or UTC). When you print an object of java.util.Date, its toString method returns the date-time in the JVM's timezone, calculated from this milliseconds value. If you need to print the date-time in a different timezone, you will need to set the timezone to SimpleDateFormat and obtain the formatted string from it.
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* .
Using modern date-time API:
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
public class Main {
public static void main(String[] args) {
OffsetDateTime odt = OffsetDateTime.parse("2011-04-11T22:27:18.491726-05:00");
// Print the default string i.e. OffsetDateTime#toString
System.out.println(odt);
// Print the date-time in a custom format. Note: OffsetDateTime#toString drops
// seconds if it is zero
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSSSSXXX");
System.out.println(dtf.format(odt));
}
}
Output:
2011-04-11T22:27:18.491726-05:00
2011-04-11T22:27:18.491726-05:00
Note: For DateTimeFormatter, the symbol, u means year whereas the symbol, y means year-of-era. It doesn't make any difference for a year in the [AD][2] era, but it matters for a year in the BC era. Check this answer to learn more about it.
Learn more about the modern date-time API from Trail: Date Time.
* 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.
05:00 -->> 0500
and
hh --> HH // error not because of this ,but date is in 24hr format.
played_at_local = dateFormat.parse("2011-04-11T22:27:18.491726-05:00");
should be
played_at_local = dateFormat.parse("2011-04-11T22:27:18.491726-0500");
You could try this method:
http://docs.oracle.com/javase/6/docs/api/java/sql/Timestamp.html#valueOf(java.lang.String)
The key thing is that fractional digits are optional and you can use a variable number of them. However, this does not seem to account for the time zone.
From the docs:
valueOf
public static Timestamp valueOf(String s)
Converts a String object in JDBC timestamp escape format to a Timestamp value.
Parameters:
s - timestamp in format yyyy-mm-dd hh:mm:ss[.f...]. The fractional seconds may be omitted.
Returns:
corresponding Timestamp value
Throws:
IllegalArgumentException - if the given argument does not have the format yyyy-mm-dd hh:mm:ss[.f...]
Try this :
dTime = new SimpleDateFormat("HH:mm:ss:SS");
String sTime = (dTime.format(new java.util.Date())).toString();
Hope this help
java.time and ThreeTenABP
There is no way that SimpleDateFormat can parse your datetime string correctly. On the other hand java.time, the modern Java date and time API, supports your format out of the box.
import org.threeten.bp.OffsetDateTime;
String dateTimeString = "2011-04-11T22:27:18.491726-05:00";
OffsetDateTime playedAtLocal = OffsetDateTime.parse(dateTimeString);
System.out.println("Parsed into " + playedAtLocal);
Output is:
Parsed into 2011-04-11T22:27:18.491726-05:00
SimpleDateFormat only supports milliseconds, exactly three decimals on the seconds, not two, not four, not six (admittedly I’m unsure whether some Android versions have a version of SimpleDateFormat that can do better, but your question shows that your version cannot). SimpleDateFormat is also notoriously troublesome and long outdated, so you wouldn’t want to use it anyway.
java.time is so much nicer to work with. You notice that we didn’t even need an explicit formatter and thus didn’t need to write a format pattern string, which is always an error-prone task. Your date time string is in ISO 8601 format, and java.time classes parse ISO 8601 as their default.
Question: Can I use java.time on Android?
Yes, java.time works nicely on older and newer Android devices. It just requires at least Java 6.
In Java 8 and later and on newer Android devices (from API level 26) the modern API comes built-in.
In Java 6 and 7 get the ThreeTen Backport, the backport of the modern classes (ThreeTen for JSR 310; see the links at the bottom).
On (older) Android use the Android edition of ThreeTen Backport. It’s called ThreeTenABP. And make sure you import the date and time classes from org.threeten.bp with subpackages.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Java Specification Request (JSR) 310, where java.time was first described.
ThreeTen Backport project, the backport of java.time to Java 6 and 7 (ThreeTen for JSR-310).
ThreeTenABP, Android edition of ThreeTen Backport
Question: How to use ThreeTenABP in Android Project, with a very thorough explanation.
Wikipedia article: ISO 8601
I have a difference of how the date 1st Jan 0001 UTC is represented in Java and in Javascript
In Java:
TimeZone utcTimeZone = TimeZone.getTimeZone("UTC");
Calendar cal = Calendar.getInstance(utcTimeZone);
cal.clear();
//1st Jan 0001
cal.set(1, 0, 1);
Date date = cal.getTime();
System.out.println(date);//Sat Jan 01 00:00:00 GMT 1
System.out.println(date.getTime());// -62135769600000
In JavaScript:
var date = new Date();
date.setTime(-62135769600000);
date.toUTCString(); //"Sat, 30 Dec 0 00:00:00 GMT"
Why the date, 1 Jan 0001 UTC, that is represented by the time -62135769600000L in Java, is not represented as 1st of January when displayed in Javascript?
It looks like this is because GregorianCalendar in Java is actually a hybrid between the Gregorian calendar and the Julian calendar:
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().
If you take 1500-01-01 for example, the Java and Javascript values will be 10 days apart.
To make it a pure GregorianCalendar, you can use this:
GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
cal.setGregorianChange(new Date(Long.MIN_VALUE));
then you get a value of -62135596800000 for 0001-01-01, which gives the same date for Javascript.
Cutover calendars are a pain in the neck - they make all kinds of things odd, and are almost never useful. (I suspect that use cases where they are appropriate may have different requirements, too. I decided not to implement it for Noda Time in the end :)
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.OffsetDateTime;
import java.time.ZoneOffset;
public class Main {
public static void main(String[] args) {
System.out.println(OffsetDateTime.of(1, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC).toInstant().toEpochMilli());
}
}
Output:
-62135596800000
ONLINE DEMO
Learn more about the modern Date-Time API from Trail: Date Time.
JavaScript:
const date = new Date();
date.setTime(-62135596800000);
console.log(date.toUTCString()); // Mon, 01 Jan 0001 00:00:00 GMT
* 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.
I'm trying to initialise a Joda-Time DateTime object with the hour of 12:00 here is how I do this:
public static final long MINUTE = 60 * 1000;
public static final long HOUR = 60 * MINUTE;
DateTime defaultDate = new DateTime(HOUR * 12);
System.out.print("the hour is: " + defaultDate.getHourOfDay()) // getting 14
Why I am getting 14 and not 12? Maybe Mama didn't teach me how to read clock right?!
You're specifying a number of milliseconds since the Unix epoch, which was midnight UTC.
However, you're implicitly using the system default time zone in your DateTime, and I suspect that at the Unix epoch, your system time zone was UTC+2.
If you want to use a specific time zone, you can pass that in the constructor:
DateTime defaultDate = new DateTime(HOUR * 12, DateTimeZone.UTC);
Also, rather than using your own constants, you could either use DateTimeConstants.MILLIS_PER_HOUR or use java.util.concurrent.TimeUnit for conversions.
java.time
Quoted below is a notice from the home page of Joda-Time:
Note that from Java SE 8 onwards, users are asked to migrate to java.time (JSR-310) - a core part of the JDK which replaces this project.
Solution using java.time, the modern Date-Time API:
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
public class Main {
public static void main(String[] args) {
ZonedDateTime zdt = ZonedDateTime.of(LocalDateTime.of(LocalDate.EPOCH, LocalTime.of(12, 0)), ZoneOffset.UTC);
System.out.println(zdt);
}
}
Output:
1970-01-01T12:00Z
ONLINE DEMO
A couple of important notes:
ZonedDateTime#toString removes seconds and fraction-of-second if they are zero. If you want to display them, you can use DateTimeFormatter e.g.
String formatted = zdt.format(DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSXXX", Locale.ENGLISH));
The Z in the output is the timezone designator for zero-timezone offset. It stands for Zulu and specifies the Etc/UTC timezone (which has the timezone offset of +00:00 hours).
Learn more about the modern Date-Time API from Trail: Date Time.
What went wrong with your code?
Quoted below the is description of DateTime(long) with my emphasis:
Constructs an instance set to the milliseconds from 1970-01-01T00:00:00Z using ISOChronology in the default time zone.
Your place, Israel was at an offset of +02:00 hours in 1970 and therefore the DateTime instance was instantiated with an offset of +02:00 hours.
Demo:
import java.util.concurrent.TimeUnit;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
public class Main {
public static void main(String[] args) {
DateTime defaultDate = new DateTime(TimeUnit.HOURS.toMillis(12), DateTimeZone.UTC);
System.out.println(defaultDate);
}
}
Output:
1970-01-01T12:00:00.000Z
Another thing, which you might have already noticed from the code, is that DO NOT perform calculations yourself if there is already a standard API (e.g. TimeUnit#toMillis) available for the same.
* 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.
What time is the start of a day, say 01/01/2010?
Is it 00:00:00:000 ? or is that midnight?
[edit]
It might be a stupid question but I'm confused because I used Calendar.set(Calendar.HOUR, 0) but this gives me a time of 12:00:00.
and now I've realised I should be using HOUR_OF_DAY
The start of the day isn't always midnight. It can depend on the time zone and date. (If the clock moves forward an hour at the start of the day, it will start at 1am.)
That's why Joda-Time has things like LocalDate.toDateTimeAtStartOfDay - and they're well worth using.
But yes, normally it's at 00:00:00 which is midnight. (This can also be formatted as "12am" depending on your locale etc.)
java.time
Normally, the start of the date is 00:00 hours but it may vary because of DST. Therefore, instead of assuming it to be 00:00 hours, the safest option is to use LocalDate#atStartOfDay(ZoneId zone).
Demo:
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) {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("M/d/u", Locale.ENGLISH);
LocalDate date = LocalDate.parse("01/01/2010", dtf);
// In JVM's timezone
ZonedDateTime startOfDay = date.atStartOfDay(ZoneId.systemDefault());
System.out.println(startOfDay);
// In custom timezone
startOfDay = date.atStartOfDay(ZoneId.of("Africa/Johannesburg"));
System.out.println(startOfDay);
}
}
Output:
2010-01-01T00:00Z[Europe/London]
2010-01-01T00:00+02:00[Africa/Johannesburg]
Learn more about the the modern date-time API* from Trail: Date Time.
* 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.
ZonedDateTime from java.time
Like Arvind Kumar Avinash already does in a good answer, I recommend that you use java.time, the modern Java date and time API, for your date and time work.
If you had got a LocalDate or a string holding a date without time of day, that answer shows you how to get the start of the day (the first moment of the day). If you had already got a ZonedDateTime, you may simply use its truncatedTo method. Let’s take one of those interesting examples where the clocks are turned forward at 00:00 so the first moment of the day is 01:00:
ZonedDateTime zdt = ZonedDateTime.of(
2000, 9, 17, 15, 45, 56, 789000000, ZoneId.of("Asia/Dili"));
System.out.println("We got date and time: " + zdt);
ZonedDateTime startOfDay = zdt.truncatedTo(ChronoUnit.DAYS);
System.out.println("Start of day is: " + startOfDay);
Output:
We got date and time: 2000-09-17T15:45:56.789+09:00[Asia/Dili]
Start of day is: 2000-09-17T01:00+09:00[Asia/Dili]
What went wrong in your code?
You’ve already said it in an edit to the question, but it deserves to be mentioned in an answer too: Calendar.HOUR refers to, from the documentation:
Field number for get and set indicating the hour of the morning or
afternoon. HOUR is used for the 12-hour clock (0 - 11). …
So if your Calendar was already holding a time in the afternoon (12 noon or later), setting HOUR to 0 gives you 12 noon (12:00 on a 24 hour clock), not 12 midnight (00:00 on a 24 hour clock). Except that the time of the hour may still be non-zero, so you may also get, for example, 12:34:45.567. The Calendar class was cumbersome to work with.
In any case the Calendar class was poorly designed and is long outdated, so you shouldn’t need to worry; just don’t use that class anymore.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Documentation of Calendar.HOUR.