tl;dr
Is Android's Calendar.Day_Of_Month zero based?
No.
I get "2018-01-02" …
… reporting the date as "2018-01-01"
Your issue of 1st vs 2nd is related to something else: time zone.
Use java.time instead of those terribly troublesome legacy classes.
OffsetDateTime.of(
LocalDate.of( 2018 , Month.JANUARY , 1 ) ,
LocalTime.NOON ,
ZoneOffset.UTC
)
.toString(): 2018-01-01T12:00Z
.format(
DateTimeFormatter.ISO_LOCAL_DATE
)
2018-01-01
Or override the zone/offset.
.format(
DateTimeFormatter.ISO_LOCAL_DATE
.withZone( ZoneId.of( "Pacific/Kiritimati" ) ) // Using zone 14 hours ahead of UTC. So noon UTC is “tomorrow” in Kiribati.
)
2018-01-02
Time Zone
If here in America/Los_Angeles
time zone at 16:00 I run your code, I get 2018-01-01
. But if I change your code to set hour to 23
instead of 12
, I get 2018-01-02
as a result.
So there is an issue about time zones. What is "today" and what is "tomorrow" depends on your time zone.
Rather than stain my brain any further, let me suggest the real solution: Stop using these terrible date-time classes.
java.time
Those old date-time classes (Date
, Calendar
, SimpleDateFormat
) were supplanted years ago by the modern java.time classes.
Apparently you want noon on the first of the year. Here's how. Notice the sane numbering: Months are 1-12 for January-December (unlike the legacy classes), day-of-month is 1-31 for first-last (like the legacy classes).
Get the date.
LocalDate ld = LocalDate.of( 2018 , 1 , 1 ) ; // January 1, 2018.
Or use the more readable Month
enum.
LocalDate ld = LocalDate.of( 2018 , Month.JANUARY , 1 ) ; // January 1, 2018.
Generate a String representing that value in standard ISO 8601 format.
ld.toString(): 2018-01-01
Get the time-of-day, noon. The LocalTime
class has an constant for that.
LocalTime lt = LocalTime.NOON ;
Specify an offset-from-UTC of zero, that is, UTC itself. The ZoneOffset
class has a constant for that.
ZoneOffset offset = ZoneOffset.UTC ;
Combine to represent a moment as a OffsetDateTime
object.
OffsetDateTime odt = OffsetDateTime.of( ld , lt , offset ) ;
Generate a String representing that value in standard ISO 8601 format.
odt.toString(): 2018-01-01T12:00Z
If you want just the date portion, extract a LocalDate
.
LocalDate ld = odt.toLocalDate() ;
Or just print a String using only the date portion by defining a DateTimeFormatter
.
DateTimeFormatter f = DateTimeFormatter.ISO_LOCAL_DATE ;
String outputOdtDateOnly = odt.format( f ) ;
2018-01-01
By default, the DateTimeFormatter
object uses the offset or zone of the object it is representing with a new String. You can optionally override that offset/zone. Let's try that.
ZoneId z = ZoneId.of( "Pacific/Kiritimati" ); // Most eastern (earliest) time zone is in Kiribati. https://en.wikipedia.org/wiki/Kiribati
DateTimeFormatter fKiritimati = f.withZone( z );
String outputOdtDateOnlyInKiribati = odt.format( fKiritimati );
Be clear that we altered the formatter object, not the data object, not the OffsetDateTime
object. We added a time zone to the new formatter object where the previous formatter held a null
, as documented. And notice how java.time uses immutable objects, where a fresh object is instantiated based on the original’s values rather than alter (“mutate”) the original. So we got a second DateTimeFormatter
object based on the first but with the addition of our specified override-zone.
Let's see what we get.
System.out.println( outputOdtDateOnlyInKiribati );
2018-01-02
Surprise! Back to the issue in your Question. Parts of Kiribati are 14 hours ahead of UTC. So when it is noon in UTC, simultaneously it is 14 hours later in Pacific/Kiritimati
zone, therefore “tomorrow” the 2nd.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date
, Calendar
, & SimpleDateFormat
.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.*
classes.
Where to obtain the java.time classes?