Here is the problem I am trying to solve: Read a string from database A, convert the string into a Date object, store the Date object into database B.
EX) Database A: Read in date string "2015-03-08 02:00:00" from database A, convert into a Date object, store back into database B.
The problem here occurs because 2:00 AM is the beginning of DST in U.S. Central time, so the Data object converts 2:00 AM straight into 3:00 AM, which means 3:00 AM gets stored into database B.
Is there any way to correct this? I am not opposed to using Joda Time if necessary.
I am trying to focus on the above date, 2015-03-08 02:00:00
This is the code I am using:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.S");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
String date = "2015-03-08 02:00:00.0";
try
{
d = sdf.parse(date);
sdf.format(d);
//Insert into database here
// ---
//
}
catch (ParseException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
You have multiple issues intertwined.
You should not be reading strings from a database for date-time values, you should be reading date-time objects. There are many Questions on StackOverflow about reading/writing date-time values from/to databases, so no need to repeat here.
If you do have a string, such as "2015-03-08 02:00:00", notice the lack of any indicator of a time zone or offset. If you want to assume that string represents a time specific the US Central Time, then you must accept the fact that there is no such date-time as that because Daylight Saving Time (DST) defines that as 3 AM. At the stroke of 2 AM, the time labeling jumps to 2 AM. So there is no point in trying to get such a non-existent date-time.
Use Proper Time Zone Names
Big tip for date-time work: Avoid thinking about time zones as "Central Time" and the 3-4 letter codes like "CST". These are not standardized, nor are the unique (many duplicates), and further confuse the mess that is Daylight Saving Time. Use a proper time zone, in pattern of "continent/majorCityOrRegion".
Local Date-Time
Perhaps what you mean is what we call "local time" where the date-time is not specific to any one time zone. For example, "Christmas starts at midnight on December 25th 2015". That means a different moment in each particular time zone. Christmas dawns earlier in Paris, than Montréal, for example.
Joda-Time
Let's interpret that string as a LocalDateTime in Joda-Time. First, for convenience, we replace the SPACE with a "T" to take advantage of Joda-Time’s built-in parsers for ISO 8601 formats.
String input = "2015-03-08 02:00:00";
String inputStandardized = input.replace( " ", "T" ); // For convenience, convert input text to comply with ISO 8601 standard’s canonical format. Replace SPACE between date & time portions with "T".
Next we parse that standardized string.
LocalDateTime localDateTime = LocalDateTime.parse( inputStandardized );
Dump to console.
System.out.println( "inputStandardized: " + inputStandardized );
System.out.println( "localDateTime: " + localDateTime );
When run.
inputStandardized: 2015-03-08T02:00:00
localDateTime: 2015-03-08T02:00:00.000
This local date-time could be stored in a SQL database using the SQL type TIMESTAMP WITHOUT TIME ZONE. This type means no adjustments to UTC time zone are to be made in either getting (SELECT) or putting (INSERT / UPDATE) database values. See Postgres doc for more info on these SQL types.
Zoned Date-Time
If you meant to represent the specific moment in a specific time zone such as America/Chicago, when we need to assign that time zone. For this kind of time-zone-specific values, in your database you would use the data type TIMESTAMP WITH TIME ZONE. That type name is misleading -- it means with respect for time zone, as it adjusts incoming data to UTC. The data's original time zone is then lost.
Unfortunately, this is one of the few situations where Joda-Time lets us down. Rather than do an adjustment, Joda-Time refuses, throwing an exception. ☹
See for yourself… Let's add the following code to the example code above.
DateTimeZone zone = DateTimeZone.forID( "America/Chicago" );
DateTime dateTimeChicago = localDateTime.toDateTime( zone ); // If the input lacks an offset, then Joda-Time *assigns* the value the specified time zone. If the input has an offset, Joda-Time *adjusts* the value to the specified zone.
Dump to console.
System.out.println( "zone: " + zone );
System.out.println( "dateTime: " + dateTimeChicago );
When run.
Exception in thread "main" org.joda.time.IllegalInstantException: Illegal instant due to time zone offset transition (daylight savings time 'gap'): 2015-03-08T02:00:00.000 (America/Chicago
…
There appears to be no good generalized workaround, just hacks. Basically, if you expect a certain time zone, you make the adjustment yourself. See discussions like this, this, this, and the Joda-Time FAQ.
java.time
In Java 8 and later, we have the new built-in date-time framework in the java.time package (Tutorial). This framework was inspired by Joda-Time, and has some advantages over Joda-Time. One of those advantages is handling of this DST non-existent value problem.
String input = "2015-03-08 02:00:00";
String inputStandardized = input.replace( " ", "T" );
LocalDateTime localDateTime = LocalDateTime.parse( inputStandardized );
Let's adjust that local date-time to assign a specific time zone. The java.time framework detects the non-existent date-time and automatically slides the time-of-day forward to respect the DST transition.
ZoneId zone = ZoneId.of( "America/Chicago" );
ZonedDateTime zdt = ZonedDateTime.of( localDateTime, zone );
Dump to console.
System.out.println("inputStandardized: " + inputStandardized );
System.out.println("localDateTime: " + localDateTime );
System.out.println("zone: " + zone );
System.out.println("zdt: " + zdt );
When run.
inputStandardized: 2015-03-08T02:00:00
localDateTime: 2015-03-08T02:00
zone: America/Chicago
zdt: 2015-03-08T03:00-05:00[America/Chicago]
SQL
As said above, you can search StackOveflow for much info on getting date-times in and out of databases.
Ideally, with java.time, you could directly feed either the LocalDateTime or ZonedDateTime to your JDBC driver. But most drivers have not yet be updated to handle the java.time types. Until your driver is updated, fall back on the java.sql.* classes. Convenient conversion methods can be found on both the new and old classes bundled with Java.
java.sql.Timestamp ts = java.sql.Timestamp.valueOf( localDateTime );
…or…
Instant instant = zdt.toInstant();
java.sql.Timestamp ts = java.sql.Timestamp.from( instant );
It's usually better to store the date as milliseconds since epoch. That way, you can use a long to store the number in your database, and when you need to format the date, you can either use Joda Time's DateTime(long) constructor or just the built-in Date(long) constructor.
Related
I have one service which requires the data of date and time which I am collecting from my database.
The data in the database is stored according to the UTC time zone.
Now I want to get those data from the data and present it to the user in the time zone whatever the want it to be in or just so the data of time and data as per there systems default time zone.
As right now I have some hard coded thing that makes the changes if the Daylight Saving Time changes
For example I the DST starts it substracts 4 hours(as per EDT) from the data that is in the system and if the DST ends I have to manually change the substracted hour with 5(as per EST) hours and the present it to the user.
I am currently doing it as bellow
Here I m using
Date from java.util.Date
getObtainedDate is for getting data from the database
I have added this minus so that it will substract that amount of time from the date.
Instant ins = attachment.getObtainedDate().toInstant();
ins = ins.minus(5, Chronounit.HOURS); // without DST
Date alteredDate = Date.from(ins);
Instant ins = attachment.getObtainedDate().toInstant();
ins = ins.minus(4, ChronoUnit.HOURS); // during DST
Date alteredDate = Date.from(ins);
I want to get rid of this hard coded thing as I have to manually change the 5 and 4 whenever the DST starts and ends.
Thank you in advance
tl;dr
You said:
some time and dates data from the database and for which the time is stored in UTC timezone
and you said:
from UTC to EST without worrying about Daylight Saving Time (DST)
myResultSet
.getObject( … , OffsetDateTime.class ) // Returns a `OffsetDateTime` object, the appropriate class mapping to the standard SQL type `TIMESTAMP WITH TIME ZONE`.
.atZoneSameInstant( ZoneId.of( "America/New_York" ) ) // Returns a `ZonedDateTime` object.
If handed an object of the terribly flawed legacy class java.util.Date, immediately convert to its modern replacement class java.time.Instant. Use new conversion methods added to the old class.
Instant instant = myJavaUtilDate.toInstant() ;
ZonedDateTime zdt = instant.atZone( ZoneId.systemDefault() ) ;
Details
Your Question is confusing and convoluted. You may be trying too hard.
You said:
time and dates data from the database and for which the time is stored in UTC timezone
If your database stores moments as a date with time with an offset of zero hours-minutes-seconds from UTC, then your database table column must be of a type akin to the standard SQL type TIMESTAMP WITH TIME ZONE. If so, you should retrieve using the Java type OffsetDateTime.
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
You said:
when the DST starts the data that I am collecting should start substracting
If you want to see that same moment through the wall-clock time of a particular time zone, apply a ZoneId to get a ZonedDateTime object.
Note that EST is not a real time zone name. Perhaps you meant America/New_York.
ZoneId z = ZoneId.of( "America/New_York" ) ;
ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;
If you want to use the JVM‘s current default time zone:
ZoneId z = ZoneId.systemDefault() ;
You said:
I am currently doing it as bellow
Instant ins = …
Instant represents a moment in UTC, always in UTC. But this class does not map to any standard SQL type. Use OffsetDateTime to exchange a moment (a specific point on the timeline) with your database.
You said:
Date alteredDate = Date.from(ins);
As for either of the Date classes, java.util.Date & java.sql.Date: (a) I don’t know which you intended, and (b) neither should be used. Both Date classes are terribly flawed, and are now legacy, supplanted by the modern java.time classes.
You said:
should start substracting 4 hours from the time as in EST the DST offset is -400 and when it ends it should substract 5 hours
No need for you to do the math. No need for you to track the DST cutover dates.
Do your logging, debugging, data storage, data exchange, and most of your business logic in UTC (an offset of zero).
Apply a time zone only for presentation to the user, and where required by a particular rule in your business logic.
By using ZoneId and ZonedDateTime classes, the DST cutovers and adjustments are handled for you.
I manage to get the time as per the timezone where the system is using this code.
This code also manages to change the offset as per the timezone.
I am here using
Date from java.util.Date
ZonedDateTime from java.time.ZonedDateTime
DateTimeFormatter from java.time.DateTimeFormatter
Instant from java.time.Instant
The code for solving this issue
Date date = attachment.getObtainedDate(); // for getting the date in the system
// Here I m setting the zone to system default
ZoneId zoneId = ZoneId.systemDefault(); // for setting the zoneId as per the location you want
ZonedDateTime zonedDateTime = Instant.ofEpochMilli(date.getTime()).atZone(zoneId);
// for formatting date and time
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("MM/dd/yyyy HH:mm");
String requiredDate = zonedDateTime.format(dtf);
If you want to check the offset for the time zone while debugging for more information, you can add one more line to the code
ZoneOffset offset = zonedDateTime.getOffset();
I've tried all sorts of different conversions with different Java formatters but I'm still not having any luck with something that seems simple.
I have a string that is a date/time in UTC. I'm trying to convert that to another time zone. Is any one able to tell me why the below isn't working? The time zone is changing but it's not changing the right way.
Updated: (though it doesn't seem like I'm setting the time zone to UTC properly as the conversion isn't correct either).
String dateInput = "2021-02-16 20:57:43";
SimpleDateFormat mdyUtc = new SimpleDateFormat ("yyyy-MM-dd hh:mm:ss");
mdyUtc.setTimeZone(TimeZone.getTimeZone("UTC");
Date utcOutput = mdyUtc.parse(dateInput);
SimpleDateFormat mdyOffset = new SimpleDateFormat ("yyyy-MM-dd hh:mm:ss");
mdyOffset.setTimeZone(TimeZone.getTimeZone("GMT-10:00");
Date localOutput = mdyOffset.parse(dateInput);
System.out.print("UTC date = " + utcOutput);
System.out.print("Changed date = " + localOutput);
Output:
UTC date = Tue Feb 16 15:57:43 EST 2021
Changed date = Wed Feb 17 01:57:43 EST 2021
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*.
Using the modern date-time API:
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) {
String dateInput = "2021-02-16 20:57:43";
// Replace ZoneId.systemDefault() with ZoneOffset.UTC if this date-time is in UTC
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("u-M-d H:m:s", Locale.ENGLISH)
.withZone(ZoneId.systemDefault());
ZonedDateTime zdt = ZonedDateTime.parse(dateInput, dtf);
ZonedDateTime result = zdt.withZoneSameInstant(ZoneId.of("GMT-10:00"));
System.out.println(result);
}
}
Output:
2021-02-16T10:57:43-10:00[GMT-10:00]
ONLINE DEMO
Learn more about the modern date-time API from Trail: Date Time.
Can I get java.util.Date from ZonedDateTime?
If at all you need to use java.util.Date, you can convert ZonedDateTime into it as follows:
Date date = Date.from(result.toInstant());
Note that 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.
* 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.
tl;dr
LocalDateTime // Represent a date with time-of-day but lacking the context of a time zone or offset-from-UTC.
.parse( // Interpret some text in order to build a date-time object.
"2021-02-16 20:57:43".replace( " " , "T" ) // Convert to standard ISO 8601 string to parse by default without needing to specify a formatting pattern.
) // Returns a `LocalDateTime` object.
.atOffset( // Place that date with time into the context of an offset. Determines a moment, a specific point on the timeline.
ZoneOffset.UTC // A constant for an offset of zero hours-minutes-seconds.
) // Returns an `OffsetDateTime` object.
.atZoneSameInstant( // Adjust the view of this moment as seen in the wall-clock time of some other time zone. Still the same moment, same point on the timeline.
ZoneId.of( "Pacific/Honolulu" ) // Use a time zone, if known, rather than a mere offset.
) // Returns a `ZonedDateTime` object.
.toString() // Generate text representing this moment in standard ISO 8601 format extended to append the time zone name in square brackets.
See this code run live at IdeOne.com.
2021-02-16T10:57:43-10:00[Pacific/Honolulu]
Details
The Answer by Avinash is correct, using a DateTimeFormatter with an assigned ZoneId. That works, but I prefer keeping the zone assignment separate from the formatter, to be more explicit to someone reading the code. This is only about my preference, not about correctness; both Answers are equally correct.
Parse your input as a LocalDateTime, as the input represents a date with time-of-day but lacks any indication of offset or time zone.
By default, the java.time classes use standard text formats defined in ISO 8601. If an input complies, no need to specify a formatting pattern. To comply, replace your input’s SPACE character in the middle with a T.
String input = "2021-02-16 20:57:43".replace( " " , "T" ) ;
LocalDateTime ldt = LocalDateTime.parse( input ) ;
You said you know for certain that input was meant to represent a date with time as seen in UTC, having an offset-from-UTC of zero hours-minutes-seconds. So we can apply an offset of zero using ZoneOffset to produce a OffsetDateTime.
Also, I suggest you educate the publisher of your data feed about using ISO 8601 formats to communicate that offset-of-zero fact by appending a Z (as well as using T in the middle).
OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC ) ; // Place date with time into context of an offset of zero.
Lastly, you said you want to adjust that moment to another time zone. Apply a ZoneId to get a ZonedDateTime object.
Actually, you specified an offset of "GMT-10:00". But it is better to use a time zone if known rather than a mere offset. A time zone is a history of past, present, and future changes to the offset used by the people of a particular region.
I will guess you want Hawaii time, Pacific/Honolulu.
ZoneId z = ZoneId.of( "Pacific/Honolulu" ) ;
ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;
The java.util.Date API is deprecated; you should look into the new Date and Time APIs around LocalTime et al.
That said, if you want to keep the old code: It is a bit brittle. Your initial date input does not specify a time zone, so you'll probably get the system's time zone. You should specify a time zone --- if the expected input is UTC, say so.
Then you need to specify the time zone either in an hour offset or with a name, not both.
When I change your code to use
mdyOffset.setTimeZone(TimeZone.getTimeZone("-10:00"));
I get
Changed date = Tue Feb 16 14:57:43 CST 2021
which seems to fit, as I'm on CST (currently 6 hours after GMT), so 20:57:43 minus 6 is 14:57:43. Again, this is displayed in my local time zone. You may have to use a DateFormat to adjust the output as needed.
I am new to Java. I have a web application that runs on a godaddy server in the USA.
The problem is when we use the application anywhere we need to update the date with the respective timestamp in database. While updating it is storing as MST format date instead of IST format (when I tried from India). Let me know how can I solve this.
Thanks in advance.
My code is as follows.
SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
f.setTimeZone(TimeZone.getTimeZone("IST"));
String date = f.format(new Date());
Date aptDate = formatter.parse(date);
System.Out.Print("The IST time is : "+ date);
Here I am getting the string format date, I want to convert into a date object and then store in the database.
When I apply the conversion it is giving earlier date. Let me what is the wrong going.
Your Question is confusing. Here are some guidelines to re-orient your thinking.
You are using old date-time classes now outmoded by the java.time framework built into Java 8 and later. Avoid the old classes as they are poorly designed, confusing, and troublesome.
Stop using the 3-4 letter codes such as IST. Do you mean India Standard Time or Irish? These codes are neither standardized nor unique. Erase these codes from both your thinking and your code. Use proper time zone names in the format of continent/region.
Do most of your work, your business logic, data exchange, data storage, and database in UTC. Apply a time zone only when expected by your user or data sink.
Use date-time types when defining columns in your database. Do not store date-time values as strings.
Use the java.sql types to transfer date-time values in and out of the database. Convert immediately to java.time types. Eventually JDBC drivers will be updated to use java.time types directly. Until that day, use new methods added to the old java.sql classes for convenient conversions.
An Instant is a moment on the timeline in UTC.
Instant now = Instant.now();
Convert to java.sql.Timestamp to write to the database.
java.sql.Timestamp ts = java.sql.Timestamp.from( now );
To the other direction, for retrieval, convert from java.sql to java.time.
Instant instant = myJavaSqlTimestamp.toInstant();
Notice that for database work we do not care about time zone as the values are all in UTC on all sides: Java, JDBC, SQL, database.
To view the wall-clock time for some locality, apply a time zone (ZoneId) to generate a ZonedDateTime object.
ZoneId zoneIdEdmonton = zoneId.of( "America/Edmonton" );
ZonedDateTime zdtEdmonton = ZonedDateTime.ofInstant( instant , zoneIdEdmonton );
You can apply yet another time zone. Note that java.time uses immutable objects. An new object arises from an old object rather than changing (“mutating”) the original. In this example, the zdtEdmonton object begets the zdtKolkata object.
ZoneId zoneIdKolkata = ZoneId.of( "Asia/Kolkata" );
ZonedDateTime zdtKolkata = zdtEdmonton.withZoneSameInstant( zoneIdKolkata );
I have a Date field in my class that can has two types of values: with and without time. Something like this: 2015-01-01 and 2015-01-01 12:00:00. I want to make formatted string from my date. I know I can use SimpleDateFormat class for doing this, but I don't know the format. In fact, If my date has the time part, I must use yyyy-MM-dd HH:mm:ss format and if my date does not have the time part, I must use yyyy-MM-dd format. My question is, Is there anyway to check a date has time section before formatting it?
Here is my code:
private SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH);
private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH);
.....
private String formatDate(Date date){
//I need to something like this:
if(/* `date` has time part */){
return dateTimeFormat.format(date);
}
else{
return dateFormat.format(date);
}
}
You cannot reliably do that, because once you create a Date object, it is represented as a number in milliseconds, which includes the specific time. For this reason you cannot possibly know how the object was built and if the specific time was set.
A workaround would be to check if the hours, minutes and seconds are set to zero. Keep in mind that there is a small probability that the date was parsed as "yyyy-MM-dd HH:mm:ss", but all these values were set to 0, simply because the time was indeed 00:00:00. However, this probability is equal to 1 / (24 * 60 * 60) = 0.00001157407, so I assume that you can live with that.
A Big Mess
As shown in the other answers, the old date-time classes bundled with Java such as java.util.Date and java.sql.Date are a mess. They were rushed to market, with bad design decisions. Specifically a java.util.Date represents both a date and a time-of-day, while its subclass java.sql.Date pretends not to have a time-of-day but actually does. The doc explains that you are supposed to ignore this inheritance relationship to help maintain the illusion. Not good.
java.time
This whole mess has been supplanted by the java.time framework built into Java 8 and later. The new classes are inspired by the highly successful Joda-Time framework, intended as its successor, similar in concept but re-architected. Defined by JSR 310. Extended by the ThreeTen-Extra project. See the Tutorial.
Date-Only
Among the new classes is LocalDate. This is the first class bundled with Java for representing a date only, without time-of-day nor time zone. To determine a date such as "today", you need a time zone (a ZoneId). For example, a new day dawns earlier in Paris than in Montréal. When you need a date-only value, I suggest you add a member to your class of this type.
LocalDate today = LocalDate.now( ZoneId.of( "America/Montreal" ) );
Date-Time
If you want a date-time, first consider the Instant class. This marks a moment on the timeline in UTC. Almost always best to do your business logic and data storage in UTC. When you need a specific moment in time rather than a vague date, add a member of this type to your class.
Instant now = Instant.now();
For presentation to the user in their desired/expected time zone, apply a time zone to an Instant to get a ZonedDateTime.
ZonedDateTime zdt = ZonedDateTime.ofInstant( now , ZoneId.of( "America/Montreal" ) );
First Moment Of The Day
I do not recommend this strategy, but to directly answer your Question about detecting if the time-of-day in a date-time value happens to be the first moment of the day…
First you need to think about time zone. All of these date-time classes mentioned above track time by a count-from-epoch in UTC. The old classes count in milliseconds, the new in nanoseconds. The epoch for both old and new is the first moment of 1970 in UTC. So there is no such thing as a date-time without a time, as you pose it in the Question. The closest thing to that is a date-time whose time-of-day happens to be the first moment of the day. Seems to be your situation (though my discussion above strongly recommends you change your situation).
How to determine if a date-time has a time-of-day that is the first moment of the day? First you must consider time zone. Either you want UTC or you want a particular time zone such as America/Montreal. Depends on your business rules.
If starting with a java.util.Date, first convert to java.time.
Instant instant = myJUDate.toInstant();
Be aware that a date does not always start at the time 00:00:00.0. Because of Daylight Saving Time (DST), and possibly other anomalies, in some places the first moment of the date is a different wall-clock time. The java.time framework can determine this first moment of the day by using the LocalDate class and its atStartOfDay methods.
So after determining the time zone we care about, we adjust our Instant into a ZonedTimeZone.
Instant instant = Instant.now ();
ZoneId zoneId = ZoneId.of ( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant ( instant , zoneId );
Next we need to see if that is first moment of the day. So we convert to a LocalDate, then back to another ZonedDateTime by calling atStartOfDay. Comparing the first ZonedDateTime to the second tells us if the original was indeed at the start of the day. To sum it up: We are converting from ZonedDateTime → LocalDate → ZonedDateTime.
// Convert to LocalDate, to get start of day, to compare to above.
LocalDate localDate = zdt.toLocalDate ();
ZonedDateTime startOfDay = localDate.atStartOfDay ( zoneId );
Boolean isStartOfDay = ( zdt.isEqual ( startOfDay ) );
Dump to console.
System.out.println ( "instant: " + instant + " for zoneId: " + zoneId + " is zdt: " + zdt + " if compared to startOfDay: " + startOfDay + " is T/F: " + isStartOfDay );
instant: 2015-12-12T23:20:23.560Z for zoneId: America/Montreal is zdt: 2015-12-12T18:20:23.560-05:00[America/Montreal] if compared to startOfDay: 2015-12-12T00:00-05:00[America/Montreal] is T/F: false
If you want UTC rather than a particular time zone, in the code above use the constant ZoneOffset.UTC as your ZoneId object. ZoneOffset is a subclass of ZoneId.
Assuming you're using java.sql.Date which derives from java.util.Date there is no possibility of a Date object not having a time value.
Note the documentation:
http://docs.oracle.com/javase/7/docs/api/java/sql/Date.html
A Date object instance holds a miliseconds value, to be precise the difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC.
Use a Calendar object. The calendar can give you structured access to all fields of a Date value, i.e. year, month, day, hours, minutes, seconds, etc. This would allow you to check whether the time fields are non-zero. As JB Nizet stated, the time part can happen to be zero, in which case wou would misinterpret it as a date only value.
When entering the brazilian DST time period, the clocks are forward 1 hour. In 2014, DST began at 19/10, so the time 19/10/2014 00:00:00 became 19/10/2015 at 01:00:00. The period between "does not exist".
Because of this, when parsing the date "19/10/2014 00:45:00" using the timezone America/Sao_Paulo, it's thrown a parsing exception: java.text.ParseException: Unparseable date: "19/10/2014 00:45:00".
String date = "19/10/2014 00:59:00";
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
sdf.setLenient(false);
sdf.setTimeZone("America/Sao_Paulo");
Calendar calendar = Calendar.getInstance();
calendar.setTimeZone(TimeZone.getTimeZone("America/Sao_Paulo"));
calendar.setTime(sdf.parse(date));
America/Sao_Paulo timezone supposedly supports DST changes. What is the expected fix for this problem? I must change manually the jvm timezone when the DST period starts and ends? Currently the "fix" is changing the jvm timezone to GMT-2 when the DST period starts.
Note: This issue originated in an application developed with spring. The example date was throwing exception when it was being converted to a java.util.Calendar from a String. In the example code above, I set lenient to false in order to be able to reproduce the error.
java.util.Calendar represents an instant in time. That instant has to exist. When local time values fall into a spring-forward DST gap, those values have no representation as a real instant in time. In other words, a properly configured clock in Brazil will never show 00:45:00 on 19/10/2014. Thus the exception. See the DST tag wiki for a visual representation.
Since you are parsing user input, I recommend parsing the string to a LocalDateTime instead of a Calendar. For Java 7, you can get this from Joda-Time. For Java 8, this is built in to the new java.time package.
Once you have it as a LocalDateTime, then you can decide where to go from there. If the time is invalid (falling into the gap of the spring-forward transition), or ambiguous (due to the fall-back transition), you can detect these scenarios and decide how to handle them in your application.
tl;dr
Use java.time to adjust from 00:45 to 01:45 in accounting for the DST cutover.
LocalDateTime.parse(
"19/10/2014 00:45:00" ,
DateTimeFormatter.ofPattern( "dd/MM/uuuu HH:mm:ss" ) // Returns a `DateTimeFormatter` object.
) // Returns a `LocalDateTime` object.
.atZone(
ZoneId.of( "America/Sao_Paulo" ) // Returns a `ZoneId` object.
) // Returns a `ZonedDateTime` object.
.toString() // Returns a `String` object holding text in standard ISO 8601 format extended to append the name of the time zone in square brackets.
2014-10-19T01:45-02:00[America/Sao_Paulo]
java.time
The modern approach uses the java.time classes that years ago supplanted the terrible date-time classes that are now legacy.
Your input string lacks an indicator of time zone or offset-from-UTC. So parse as a LocalDateTime.
String input = "19/10/2014 00:45:00";
DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd/MM/uuuu HH:mm:ss" );
LocalDateTime ldt = LocalDateTime.parse( input , f );
A LocalDateTime is just a date with time-of-day. So this class cannot represent a moment, is not a point on the timeline. To determine a moment, we must place the LocalDateTime in the context of a time zone, thereby producing a ZonedDateTime object.
ZoneId z = ZoneId.of( "America/Sao_Paulo" );
ZonedDateTime zdt = ldt.atZone( z );
Dump to console.
System.out.println( "ldt = " + ldt );
System.out.println( "zdt = " + zdt );
When run.
ldt = 2014-10-19T00:45
zdt = 2014-10-19T01:45-02:00[America/Sao_Paulo]
We can see that java.time made the necessary adjustment. The time-of-day of 00:45 was changed to 01:45.
Be sure to understand the logic used by java.time in this adjustment. Study the Javadoc. Only you can decide if such an adjustment is the right thing to do for your business logic.
Is date originated from user input or stored information? Note that setting GMT-3 to JVM is not the same as "America/Sao_Paulo". I don't believe GMT observes daylight saving times. Switching JVM setting back and forth doesn't look like a good solution. If it's just stored information you could update the value 1 hour ahead ou backwards, not sure which is the case here. Setting GMT-3 timezone was the only explanation I see for ending up having an invalid date in America/Sao_Paulo timezone.