I'd like to use Joda-Time to parse a date with an optional timezone: 2014-08-08+02:00
I tried ISODateTimeFormat.dateTimeParser().parseDateTime(date); but that gives me the following error:
java.lang.IllegalArgumentException: Invalid format: "2014-08-08+02:00" is malformed at "+02:00"
I'm not aware of a better way to handle optional data than to have several patterns and parse them in turn. See code example below:
public static void main(String[] args) throws Exception {
System.out.println(parseWithOptionalTZ("2014-08-08+02:00"));
System.out.println(parseWithOptionalTZ("2014-08-08"));
}
private static DateTime parseWithOptionalTZ(String date) {
DateTimeFormatter[] formatters = {
DateTimeFormat.forPattern("YYYY-MM-dd"),
DateTimeFormat.forPattern("YYYY-MM-ddZ")
};
for (DateTimeFormatter dateTimeFormatter : formatters) {
try {
return dateTimeFormatter.parseDateTime(date);
} catch (IllegalArgumentException e) {
// ignore
}
}
throw new IllegalArgumentException("Could not parse: " + date);
}
Output:
2014-08-07T23:00:00.000+01:00
2014-08-08T00:00:00.000+01:00
Time Zone Is Crucial
Do not ignore the time zone or offset, as mentioned in the comments.
Ignoring time zone on a date-time is like ignoring the C/F (Celsius/Fahrenheit) on a temperature reading or ignoring the character set encoding of text. You will be misinterpreting your data.
Parsing With Time Zone
Specifying a time zone during parsing can have two different effects in Joda-Time. Mull this over and you’ll realize it is logical and is doing the right thing.
After Parsing
If you specify a time zone on a formatter that parses an input containing an offset or time zone, then Joda-Time pays attention to the contained offset during the parsing. After the date-time is determined (where it falls on the timeline of the Universe defined as number of milliseconds since the beginning of 1970 in UTC time zone, that is, no time zone), then your specified time zone is assigned to the DateTime object. In this case, passing the specified time zone did not affect the parsing. The specified time zone affects the generation of String representations of that date-time value.
During Parsing
If, on the other hand, you specify a time on a formatter that parses an input lacking any offset or time zone information, then Joda-Time uses your specified time zone during the parsing of the input String.
Example Code, Joda-Time
Here is example code using Joda-Time 2.3. First is the right way, using the offset during parsing of the string. Second is the wrong way, ignoring the offset. Notice the different results, different date-time values, even possibly different dates (depending on the time zone used to create String representations).
With Offset
String input = "2014-08-08+02:00";
DateTimeFormatter formatter = DateTimeFormat.forPattern( "yyyy-MM-ddZ" ).withZone( DateTimeZone.UTC );
DateTime dateTimeUsingOffset = formatter.parseDateTime( input ); // Offset used to determine date-time during parsing. After parsing the UTC time zone is assigned.
Without Offset
String inputTruncatedOffset = input.substring( 0, 10 );
DateTime dateTimeIgnoringOffset = new DateTime( inputTruncatedOffset, DateTimeZone.UTC ); // In contrast to above, the UTC time zone is used *during* parsing to determine date-time as the input string contained no hint about offset or time zone.
Dump to console.
System.out.println( "input: " + input );
System.out.println( "dateTimeUsingOffset: " + dateTimeUsingOffset );
System.out.println( "inputTruncatedOffset: " + inputTruncatedOffset );
System.out.println( "dateTimeIgnoringOffset: " + dateTimeIgnoringOffset );
When run.
input: 2014-08-08+02:00
dateTimeUsingOffset: 2014-08-07T22:00:00.000Z
inputTruncatedOffset: 2014-08-08
dateTimeIgnoringOffset: 2014-08-08T00:00:00.000Z
Related
I have a method taking Date field as a input parameter.
public static String formatDate(Date inputDate) {
// if user send the date with inputDate= new Date(00000000) or new Date(0L) because it is Java default date.
I have to send the exception error with message Invalid date.
}
What I did is something as below, But I am unable to get the error while passing the invalid date of zero count-from-epoch like "new Date( 0L )" as inputDate parameter.
public static String formatDate(Date inputDate) {
if (null == inputDate)
throw new FieldFormatException("Empty date field.");
try {
SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");
return formatter.format(inputDate);
} catch (Exception ex) {
throw new FieldFormatException("Exception in formatting date field." + ex);
}
}
It sounds like you just want:
if (inputDate == null || inputDate.getTime() == 0L)
That will detect if inputDate is null or represents the Unix epoch.
As noted in the comments though:
Rejecting a single value is kinda dangerous - why is new Date(0) "wrong" but new Date(1) "right"?
This prevents you accepting legitimate input that happens to be the Unix epoch
The accepted Answer by Skeet is correct.
tl;dr
input.equals( Instant.EPOCH )
java.time
You are using troublesome old date-time classes that are now supplanted by the java.time classes.
The Instant class takes the place of Date as a moment on the timeline in UTC but with a finer resolution of nanoseconds rather than milliseconds.
You can convert when interfacing with legacy code not yet updated to java.time classes. To convert, call new methods added to the old classes.
Instant instant = myDate.toInstant() ;
Check for null.
if ( null == input ) {
throw new IllegalArgumentException( "Received invalid input: null." ) ;
}
Check for that count-from-epoch value zero that seems to be of special concern to you. The Instant class has a constant for that, for a count of zero nanoseconds from the epoch reference date-time used by Unix and Java: first moment of 1970 in UTC.
if ( input.equals( Instant.EPOCH ) ) {
throw new IllegalArgumentException( "Received invalid input: Instant.EPOCH. Input must be later than 1970-01-01T00:00:00Z." ) ;
}
You might want to check for recent date-time values, if required by your business rules.
if ( input.isBefore( Instant.now() ) ) {
throw new IllegalArgumentException( "Received invalid input. Input must not be in the past." ) ;
}
When generating a string take into account time zone. For any given moment, the date and the time-of-day vary around the globe by zone.
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
Your desired format happens to be the "basic" version of a standard ISO 8601 format. This format is predefined as a constant: DateTimeFormatter.BASIC_ISO_DATE.
String output = zdt.format( DateTimeFormatter.BASIC_ISO_DATE ) ;
how to add user timezone to utc
i am getting utc date like this
DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-ddHH:mm:ss");
DateTime dateTime = formatter.withOffsetParsed().parseDateTime(getval[2]);
DateTime dateTimeUtc = dateTime.toDateTime(DateTimeZone.UTC);
Now i want to get user Timezone and add it to utc to convert that to localtime
UPDATE
i was able to get the user timezone but could add it to the utc
DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-ddHH:mm:ss");
DateTime dateTime = formatter.withOffsetParsed().parseDateTime(getval[2]);
java.util.Calendar now = java.util.Calendar.getInstance();
java.util.TimeZone timeZone = now.getTimeZone();
DateTimeZone dtZone = DateTimeZone.forID(timeZone.getID());
DateTime dateTimeUtc = dateTime.toDateTime(DateTimeZone.UTC);
ofm.setDate(dateTimeUtc.toDateTime(dtZone).toDate());
This below code may help you to get the time zone of the user
//get Calendar instance
Calendar now = Calendar.getInstance();
//get current TimeZone using getTimeZone method of Calendar class
TimeZone timeZone = now.getTimeZone();
//display current TimeZone using getDisplayName() method of TimeZone class
System.out.println("Current TimeZone is : " + timeZone.getDisplayName());
also the below link helps you to convert user's timezone to UTC
link
java.time
The Joda-Time project was succeeded by the java.time framework defined in JSR 310. Here is the modern solution using those new classes found in Java 8 and later.
Your input format is nearly compliant with the ISO 8601 standard. The data is just missing the T between the date portion and the time-of-day portion, and is missing a Z on the end to indicate UTC. See if you can educate the publisher of your data about this important standard.
String input = "2019-01-23T01:23:45.123456789Z" ;
The java.time classes use the standard formats by default. So no need to specify a formatting pattern.
Instant instant = Instant.parse( input ) ;
instant.toString() = 2019-01-23T01:23:45.123456789Z
If you can get the input format changed, define a formatting pattern to match.
DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu-MM-ddHH:mm:ss" ) ;
Lacking any indicator of time zone or offset, we must parse as a LocalDateTime. Note that such an object does not represent a moment, is not a specific point on the timeline.
String input = "2019-01-2301:23:45" ;
LocalDateTime ldt = LocalDateTime.parse( input , f ) ;
ldt.toString() = 2019-01-23T01:23:45
You claim to be sure this date and time were intended to represent a moment in UTC. So we can apply an offset using the constant ZoneOffset.UTC to produce a OffsetDateTime.
OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC ) ;
odt.toString() = 2019-01-23T01:23:45Z
Then you said you want to adjust this into a specific time zone. Same moment, same point on the timeline, but different wall-clock time.
Specify a proper time zone name in the format of Continent/Region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 2-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;
zdt.toString() = 2019-01-23T02:23:45+01:00[Africa/Tunis]
As you can see, Tunisia on that date was running an hour ahead of UTC. So the time-of-day appears to be 2 AM rather than 1 AM.
Here's a small example that gets the difference from a list of time zones (in hours):
import java.util.Date;
import java.util.TimeZone;
public class StackOverflowTimeZone {
public static void main(String[] a) {
Date date = new Date();
for(int index = 0; index < TimeZone.getAvailableIDs().length; index++) {
System.out.println(TimeZone.getAvailableIDs()[index] + " offset from UTC: " + TimeZone.getTimeZone(TimeZone.getAvailableIDs()[index]).getOffset(date.getTime()) / (60 * 60 * 1000) + " hours.");
}
}
}
The abstract class TimeZone was designed to get the offset of a designated time zone from Coordinated Universal Time (UTC). There is a list of time zones that can be found by using the method TimeZone.getAvailableIDs(). After getting the offset, you will need to do a few small calculuations in order to find out whether the designated time zone is ahead or behind UTC. The sign (+/-) of your output should correlate to whether that designated time zone is ahead or behind UTC.
I'm a beginner in java programmeing.
I want to parse a complex date format : YYYY-MM-DDthh:mm:ssTZD, for example 2014-09-24T21:32:39-04:00
I tried this :
String str_date="2014-09-24T21:32:39-04:00";
DateFormat formatter = new SimpleDateFormat("YYYY-MM-DDthh:mm:ss");
Date date = (Date)formatter.parse(str_date);
But for the timezone part (-04:00), i have no idea what to put (after the :ss)
Any help ?
If you are using Java < 7 then you'd need to remove ':' from your input and parse it:
Here is an example:
public static void main(String[] args) throws ParseException {
String str_date="2014-09-24T21:32:39-04:00";
str_date = str_date.replaceAll(":(\\d\\d)$", "$1");
System.out.println("Input modified according to Java 6: "+str_date);
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
Date date = (Date)formatter.parse(str_date);
System.out.println(date);
}
prints:
Input modified according to Java 6: 2014-09-24T21:32:39-0400
Wed Sep 24 21:32:39 EDT 2014
Java7
SimpleDateFormat documentation lists the timezone as z, Z, and X and for you it looks like you want XXX.
Java6
Java7 added X specifier, but 6 still has the Z and z. However, you will have to modify the string first so that it either has no colon in the timezone or has GMT before the -:
String str_date="2014-09-24T21:32:39-04:00";
int index = str_date.lastIndexOf( '-' );
str_date = str_date.substring( 0, index ) + "GMT" + str_date.substring( index+1 );
Then you can use the format specifier z
ISO 8601
Your string complies with the ISO 8601 standard. That standard is used as the default in parsing and generating strings by two excellent date-time libraries:
Joda-Time
java.time package (bundled with Java 8, inspired by Joda-Time, defined by JSR 310)
Merely pass your string to the constructor or factory method. A built-in formatter is automatically used to parse your string.
Unlike java.util.Date, in these two libraries a date-time object knows its own assigned time zone. The offset from UTC specified at the end of your string is used to calculate a number of milliseconds (or nanoseconds in java.time) since the Unix epoch of the beginning of 1970 in UTC time zone. After that calculation, the resulting date-time is assigned a time zone of your choice. In this example I arbitrarily assign a India time zone. For clarity this example creates a second date-time object in UTC.
Joda-Time
DateTimeZone timeZoneIndia = DateTimeZone.forID( "Asia/Kolkata" );
DateTime dateTimeIndia = new DateTime( "2014-09-24T21:32:39-04:00" , timeZoneIndia );
DateTime dateTimeUtc = dateTimeIndia.withZone( DateTimeZone.UTC );
I have a date string in this format:
String fieldAsString = "11/26/2011 14:47:31";
I am trying to convert it to a Date type object in this format: "yyyy.MM.dd HH:mm:ss"
I tried using the following code:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
Date newFormat = sdf.parse(fieldAsString);
However, this throws an exception that it is an Unparsable date.
So I tried something like this:
Date date = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss").parse(fieldAsString);
String newFormat = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss").format(date)
However, this new format is now in the 'String' format but I want my function to return the new formatted date as a 'Date' object type. How would I do this?
Thanks!
You seem to be under the impression that a Date object has a format. It doesn't. It sounds like you just need this:
Date date = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss").parse(fieldAsString);
(You should consider specifying a locale and possibly a time zone, mind you.)
Then you've got your Date value. A format is only relevant when you later want to convert it to text... that's when you should specify the format. It's important to separate the value being represent (an instant in time, in this case) from a potential textual representation. It's like integers - there's no difference between these two values:
int x = 0x10;
int y = 16;
They're the same value, just represented differently in source code.
Additionally consider using Joda Time for all your date/time work - it's a much cleaner API than java.util.*.
The answer by Jon Skeet is correct and complete.
Internal to java.util.Date (and Date-Time seen below), the date-time value is stored as milliseconds since the Unix epoch. There is no String inside! When you need a textual representation of the date-time in a format readable by a human, either call toString or use a formatter object to create a String object. Likewise when parsing, the input string is thrown away, not stored inside the Date object (or DateTime object in Joda-Time).
Joda-Time
For fun, here is the (better) way to do this work with Joda-Time, as mentioned by Mr. Skeet.
One major difference is that while a java.util.Date class seems to have a time zone, it does not. A Joda-Time DateTime in contrast does truly know its own time zone.
String input = "11/26/2011 14:47:31";
// From text to date-time.
DateTimeZone timeZone = DateTimeZone.forID( "Pacific/Honolulu" ); // Time zone intended but unrecorded by the input string.
DateTimeFormatter formatterInput = DateTimeFormat.forPattern( "MM/dd/yyyy HH:mm:ss" ).withZone( timeZone );
// No words in the input, so no need for a specific Locale.
DateTime dateTime = formatterInput.parseDateTime( input );
// From date-time to text.
DateTimeFormatter formatterOutput_MontréalEnFrançais = DateTimeFormat.forStyle( "FS" ).withLocale( java.util.Locale.CANADA_FRENCH ).withZone( DateTimeZone.forID( "America/Montreal" ) );
String output = formatterOutput_MontréalEnFrançais.print( dateTime );
Dump to console…
System.out.println( "input: " + input );
System.out.println( "dateTime: " + dateTime );
System.out.println( "dateTime as milliseconds since Unix epoch: " + dateTime.getMillis() );
System.out.println( "dateTime in UTC: " + dateTime.withZone( DateTimeZone.UTC ) );
System.out.println( "output: " + output );
When run…
input: 11/26/2011 14:47:31
dateTime: 2011-11-26T14:47:31.000-10:00
dateTime as milliseconds since Unix epoch: 1322354851000
dateTime in UTC: 2011-11-27T00:47:31.000Z
output: samedi 26 novembre 2011 19:47
Search StackOverflow for "joda" to find many more examples.
I have three database (SQLServer) fields:
startDate (Date)
endDate (Date)
Duration (text).
I am calculating date and time difference by using java following code.
public static void main(String[] args) {
DateTimeUtils obj = new DateTimeUtils();
SimpleDateFormat simpleDateFormat =
new SimpleDateFormat("dd/M/yyyy hh:mm:ss");
try {
Date date1 = simpleDateFormat.parse("10/10/2013 11:30:10");
Date date2 = simpleDateFormat.parse("13/10/2013 20:55:55");
obj.printDifference(date1, date2);
} catch (ParseException e) {
e.printStackTrace();
}
}
public void printDifference(Date startDate, Date endDate){
//milliseconds
long different = endDate.getTime() - startDate.getTime();
String diff = "";
System.out.println("startDate : " + startDate);
System.out.println("endDate : "+ endDate);
System.out.println("different : " + different);
diff = String.format("%d:%d", TimeUnit.MILLISECONDS.toHours(different), TimeUnit.MILLISECONDS.toMinutes(different) - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(different)));
And It returns “HH:MM” and updates the Duration field as String in my database. This works great.
Now there will be multiple duration, what I want to do is once I have Duration, I would like to do add up multiple duration and it should return totalDuration:
For Example:
In my table # 1 I have,
Id | Duration
1001 | 05:04
1001 | 12:19
1001 | 02:16
Table # 2
Id | totalDuration
1001 | 19:39
My Question is: How do I convert HH:Mm to Date and add multiple records together to get totalDuration. Total duration should be text and return the same format “HH:MM
If you actually have date values for start and end in table 1 it would be a lot easier to use datediff and sum the values.
If you still have all the values you worked out then just accumulate a long millisecond count as you go and use that.
If not then there are a few ways, the simplest is probably going to be:
String[] split = duration.split(":");
long duration = Long.parseLong(split[0])*60*60*1000 + Long.parseLong(split[1])*60*1000;
You can then loop through adding up all those durations, then at the end convert it back to a String.
Really you should store the millisecond count in your database and then convert it to text to display though.
Question Not Clear
Your question is not clear. (a) Question fails to address issue of time zones. Are these date-times all in UTC/GMT? (b) Your example dates seem to be 3 days apart, yet your code returns only hours and minutes.
Joda-Time
Trying to roll your own date-time calculations usually leads to trouble and frustration. See if a good date-time library can help you, such as Joda-Time or the new java.time.* classes bundled with Java 8.
ISO 8601 Duration
The international standard for date and time, ISO 8601, defines a way to work with what it calls durations. Historically, the concept has also been called periods. A duration tracks the concept of elapsed time in terms of years, months, days, hours, and minutes.
A value is represented as a string in the format of PnYnMnDTnHnMnS. The 'P' indicates the beginning of a duration (Period) string. A 'T' indicates the time portion. Each number precedes its element designator. For example, "P3Y6M4DT12H30M5S" represents a duration of "three years, six months, four days, twelve hours, thirty minutes, and five seconds".
Joda-Time is ISO 8601 savvy, offering the Period class to represent an ISO duration. A Period can be constructed by passing an ISO duration string. Likewise, the default toString implementation on Period outputs an ISO duration string. You could store that ISO duration string in your database. Joda-Time Period instances can be added together by calling the plus method.
Example Code
Here's example code using Joda-Time 2.3 in Java 7. I sourced the two date-time strings from your question. To demonstrate addition, I add an extra 5 minutes.
Good practice dictates being explicit about time zones rather than rely on defaults. Here I use Paris, choosing so arbitrarily. You may well be using UTC/GMT (no time zone offset), in which case you may pass the pre-defined time zone DateTimeZone.UTC.
// © 2013 Basil Bourque. This source code may be used freely forever by anyone taking full responsibility for doing so.
// import org.joda.time.*;
// import org.joda.time.format.*;
DateTimeZone timeZone_Paris = DateTimeZone.forID( "Europe/Paris" ); // Or for UTC/GMT, use: DateTimeZone.UTC
DateTimeFormatter formatter = DateTimeFormat.forPattern( "dd/MM/yyyy HH:mm:ss" ).withZone( timeZone_Paris );
DateTime start = formatter.parseDateTime( "10/10/2013 11:30:10" );
DateTime stop = formatter.parseDateTime( "13/10/2013 20:55:55" );
Period period = new Period( start, stop );
DateTime now = new DateTime( timeZone_Paris );
Period period2 = new Period( now , now.plusMinutes( 5 ));
Period total = period.plus( period2 );
Dump to console…
System.out.println( "start: " + start.toString() );
System.out.println( "stop: " + stop.toString() );
System.out.println( "period: " + period.toString() );
System.out.println();
System.out.println( "now: " + now.toString() );
System.out.println( "period2: " + period2.toString() );
System.out.println();
System.out.println( "total: " + total.toString() );
When run…
start: 2013-10-10T11:30:10.000+02:00
stop: 2013-10-13T20:55:55.000+02:00
period: P3DT9H25M45S
now: 2013-12-24T01:03:10.029+01:00
period2: PT5M
total: P3DT9H30M45S
Note how the 25M increased to 30M.
Date-Time Strings
Tip: If you control those date-time strings as shown in your example code, your work will be easier if you switched to using the standard ISO 8601 format. For example: 2013-12-21T21:03:48+05:30 or 2013-12-21T21:03:48Z.
Using the "dd/M/yyyy hh:mm:ss" format is ambiguous. No reason to use that rather than the standard format. A bonus: The standard format gives a chronological order when sorting alphabetically.