Java: calculating duration - java

I created the following code to calculate the duration between two timestamps which can come in two different formats:
public class dummyTime {
public static void main(String[] args) {
try {
convertDuration("2008-01-01 01:00 pm - 01:56 pm");
convertDuration("2008-01-01 8:30 pm - 2008-01-02 09:30 am");
} catch (Exception e) {
e.printStackTrace();
}
}
private static String convertDuration(String time) throws Exception {
String ts[] = time.split(" - ");
SimpleDateFormat formatNew = new SimpleDateFormat("HH:mm");
Date beg, end;
String duration = null;
beg = getDateTime(ts[0]);
end = getDateTime(ts[1], beg);
duration = formatNew.format(end.getTime() - beg.getTime());
System.out.println(duration + " /// " + time + " /// " + beg + " /// "
+ end);
return duration;
}
private static Date getDateTime(String dateTime) throws ParseException {
DateFormat formatOldDateTime = new SimpleDateFormat(
"yyyy-MM-dd hh:mm aa");
DateFormat formatOldTimeOnly = new SimpleDateFormat("hh:mm aa");
Date date = null;
try {
date = formatOldDateTime.parse(dateTime);
} catch (ParseException e) {
date = formatOldTimeOnly.parse(dateTime);
}
return date;
}
private static Date getDateTime(String dateTime, Date orig)
throws ParseException {
Date end = getDateTime(dateTime);
if (end.getYear() == 70) {
end.setYear(orig.getYear());
end.setMonth(orig.getMonth());
end.setDate(orig.getDate());
}
return end;
}
}
The output it generates is:
01:56 /// 2008-01-01 01:00 pm - 01:56 pm /// Tue Jan 01 13:00:00 CET 2008 /// Tue Jan 01 13:56:00 CET 2008
14:00 /// 2008-01-01 8:30 pm - 2008-01-02 09:30 am /// Tue Jan 01 20:30:00 CET 2008 /// Wed Jan 02 09:30:00 CET 2008
My questions are:
Why are the results always wrong
(always +1h)?
What is a better
way to identify timestamps without
day? == 70 doesn't look good and the
getDay & setDay functions are
deprecated too.
Many many thanks, this issue has been driving me crazy for several hours.

You are formatting time of day, not number of hours and minutes. As you are in the CET timezone [Central European Time] in winter, that is one hour different from UTC ("GMT").
You probably want to be using Calendar instead of Date. Or Joda-Time.

At my computer this is off by 2 hours, because I'm at GMT+2, and you're probably at GMT+1. Note that formatNew.format(end.getTime() - beg.getTime()); receives date, i.e. treats your 56 minutes as 1970-01-01-00:56:00 GMT+1. To fix this quickly, call formatNew.setTimeZone( TimeZone.getTimeZone( "GMT" ) );
For the 2nd item, you can check if format-yyyy-MM-dd failed (you catch a parse error), and this is how you know that there's no year.

Simple answer: it's inappropriate to use SimpleDateFormat to format values that represent time of day without date.
Longer answer: Java time values are a count of milliseconds since the "epoch": midnight, January 1 1970, UTC.
SimpleDateFormat assumes that you're giving it a valid timestamp, and applies a localized conversion to a date and time. I suspect that your locale is one hour off GMT (continental Europe), so that's why you're seeing results that are one hour off.
While you could fool SimpleDateFormat by setting the timezone GMT, you're probably better off displaying durations using explicit math:
int duration = 90;
System.out.printf("%02d:%02d", duration / 60, duration % 60);

First, your example strings are not consistent: 8:30 pm lacks a padding zero. I will assume that is a typo, and should have been 08:30 pm.
Undesirable string formats
By the way, these input string formats are not desirable.
- A much better way is to use standard ISO 8601 formats.
- 12-hour clocks with AM/PM are troublesome. The standard formats use 24-hour clock, with hours 0-23.
- The standard notation for an interval is the pair of date-time strings separated by a slash: 2008-01-01T13:00/2008-01-01T13:56.
Your input strings have another serious problem: No indication of offset-from-UTC or time zone. Without an offset or time zone, we must fall back to assuming generic 24-hour days. This ignores anomalies such as Daylight Saving Time (DST) that can result in 23 or 25 hour long days.
If you know the time zone intended for the incoming strings, pass that as a second argument to get a correct result.
java.time
This Question is quite old. Since then Java has supplanted the troublesome old date-time classes (Date, Calendar, etc.) with modern java.time classes. We use java.time in the example code below.
Example class
Here is a complete class for processing these strings as given in your Question. A Duration is produced.
package javatimestuff;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Locale;
/**
*
* #author Basil Bourque
*/
public class DurationProcessor {
static final int SHORT = 30;
static final int LONG = 41;
static final DateTimeFormatter FORMATTER_LOCALDATETIME = DateTimeFormatter.ofPattern ( "uuuu-MM-dd hh:mm a" );
static final DateTimeFormatter FORMATTER_LOCALTIME = DateTimeFormatter.ofPattern ( "hh:mm a" );
static public Duration process ( String input ) {
return DurationProcessor.process ( input , ZoneOffset.UTC );
}
static public Duration process ( String input , ZoneId zoneId ) {
Duration d = Duration.ZERO; // Or maybe null. To be generated by the bottom of this code.
if ( null == input ) {
// …
System.out.println ( "ERROR - Passed null argument." );
return d;
}
if ( input.length () == 0 ) {
// …
System.out.println ( "ERROR - Passed empty string as argument." );
return d;
}
String inputModified = input.toUpperCase ( Locale.ENGLISH ); // Change `am` `pm` to `AM` `PM` for parsing.
String[] parts = inputModified.split ( " - " );
String inputStart = parts[ 0 ]; // A date-time sting.
String inputStop = parts[ 1 ]; // Either a date-time string or a time-only string (assume the same date).
ZonedDateTime start = null; // To be generated in this block of code.
try {
LocalDateTime ldt = LocalDateTime.parse ( inputStart , DurationProcessor.FORMATTER_LOCALDATETIME );
start = ldt.atZone ( zoneId );
} catch ( DateTimeParseException e ) {
// …
System.out.println ( "ERROR - The start failed to parse. inputStart: " + inputStart );
return d;
}
ZonedDateTime stop = null; // To be generated in this block of code.
switch ( input.length () ) {
case DurationProcessor.SHORT: // Example: "2008-01-01 01:00 pm - 01:56 pm"
try {
LocalTime stopTime = LocalTime.parse ( inputStop , DurationProcessor.FORMATTER_LOCALTIME );
stop = ZonedDateTime.of ( start.toLocalDate () , stopTime , zoneId );
} catch ( DateTimeParseException e ) {
// …
System.out.println ( "ERROR - The stop time failed to parse." );
return d;
}
break;
case DurationProcessor.LONG: // "2008-01-01 8:30 pm - 2008-01-02 09:30 am"
try {
LocalDateTime ldt = LocalDateTime.parse ( inputStop , DurationProcessor.FORMATTER_LOCALDATETIME );
stop = ldt.atZone ( zoneId );
} catch ( DateTimeParseException e ) {
// …
System.out.println ( "ERROR - The stop date-time failed to parse." );
return d;
}
break;
default:
// …
System.out.println ( "ERROR - Input string is of unexpected length: " + input.length () );
break;
}
d = Duration.between ( start , stop );
return d;
}
public static void main ( String[] args ) {
// Run with out time zone (assumes UTC).
Duration dShort = DurationProcessor.process ( "2008-01-01 01:00 pm - 01:56 pm" );
System.out.println ( "dShort: " + dShort );
Duration dLong = DurationProcessor.process ( "2008-01-01 08:30 pm - 2008-01-02 09:30 am" );
System.out.println ( "dLong: " + dLong );
// Run with specified time zone.
ZoneId z = ZoneId.of ( "America/Montreal" );
Duration dShortZoned = DurationProcessor.process ( "2008-01-01 01:00 pm - 01:56 pm" , z );
System.out.println ( "dShortZoned: " + dShortZoned );
Duration dLongZoned = DurationProcessor.process ( "2008-01-01 08:30 pm - 2008-01-02 09:30 am" , z );
System.out.println ( "dLongZoned: " + dLongZoned );
}
}
Note the main method within the class for example usages.
First a pair of calls without specifying a time zone. So UTC and 24-hour days will be used.
Duration dShort = DurationProcessor.process ( "2008-01-01 01:00 pm - 01:56 pm" );
System.out.println ( "dShort: " + dShort );
Duration dLong = DurationProcessor.process ( "2008-01-01 08:30 pm - 2008-01-02 09:30 am" );
System.out.println ( "dLong: " + dLong );
Another pair of calls where we do specify the intended time zone.
ZoneId z = ZoneId.of ( "America/Montreal" );
Duration dShortZoned = DurationProcessor.process ( "2008-01-01 01:00 pm - 01:56 pm" , z );
System.out.println ( "dShortZoned: " + dShortZoned );
Duration dLongZoned = DurationProcessor.process ( "2008-01-01 08:30 pm - 2008-01-02 09:30 am" , z );
System.out.println ( "dLongZoned: " + dLongZoned );
Live code
See this class run in live code in IdeOne.com.
dShort: PT56M
dLong: PT13H
dShortZoned: PT56M
dLongZoned: PT13H
As noted elsewhere on this page, your output format using time-of-day style such as 00:56 is ambiguous and confusing, and should be avoided. The Duration class instead uses standard ISO 8601 format for durations. Above, we see results of fifty-six minutes and thirteen minutes.
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.
Where to obtain the java.time classes?
Java SE 8 and SE 9 and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
The ThreeTenABP project adapts ThreeTen-Backport (mentioned above) for Android specifically.
See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Related

Java Calendar clear() changes DST

First, I want to state that I know the Java Calendar class is being supplanted by other libraries that are arguably better. Perhaps I've stumbled upon one of the reasons Calendar has fallen out of favor.
I ran into frustrating behavior in Calendar as it regards to the overlapping hour at the end of daylight savings time.
public void annoying_issue()
{
Calendar midnightPDT = Calendar.getInstance(TimeZone.getTimeZone("US/Pacific"));
midnightPDT.set(Calendar.YEAR, 2021);
midnightPDT.set(Calendar.MONTH, 10);
midnightPDT.set(Calendar.DAY_OF_MONTH, 7);
midnightPDT.set(Calendar.HOUR_OF_DAY, 0);
midnightPDT.set(Calendar.MINUTE, 0);
midnightPDT.set(Calendar.SECOND, 0);
midnightPDT.set(Calendar.MILLISECOND, 0);
Calendar oneAMPDT = Calendar.getInstance(TimeZone.getTimeZone("US/Pacific"));
oneAMPDT.setTimeInMillis(midnightPDT.getTimeInMillis() + (60*60*1000));//this is the easiest way I've found to get to the first 1am hour at DST overlap
System.out.println(new Date(midnightPDT.getTimeInMillis()));//prints the expected "Sun Nov 7 00:00:00 PDT 2021"
System.out.println(new Date(oneAMPDT.getTimeInMillis()));//prints "Sun Nov 7 01:00:00 PDT 2021" also expected
oneAMPDT.clear(Calendar.MINUTE);//minute is already 0 so no change should occur... RIGHT!?
//WRONG!!!!
//The time is now in PST! The millisecond value has increased by 3600000, too!!
System.out.println(new Date(oneAMPDT.getTimeInMillis()));//prints "Sun Nov 7 01:00:00 PST 2021"
}
Following along with the comments you'll see that clearing the MINUTE field in the calendar actually moved it up an hour! The HECK!?
This also occurs when I use oneAMPDT.set(Calendar.MINUTE, 0)
Is this expected behavior? Is there a way to prevent this?
Avoid legacy date-time classes; convert if needed
As you noted, Calendar was supplanted years ago by the java.time classes defined in JSR 310 (unanimously adopted). And as you note there are many reasons to avoid using Calendar & Date etc.
If you must have a Calendar object to interoperate with old code not yet updated to java.time, convert after doing your work in java.time.
java.time
Specify your desired time zone. Note that US/Pacific is merely an alias for the actual time zone, America/Los_Angeles.
ZoneId zLosAngeles = ZoneId.of( "America/Los_Angeles" ) ;
Specify your desired moment.
LocalDate ld = LocalDate.of( 2021 , Month.NOVEMBER , 7 ) ;
In your code, you seem to assume the first moment of the day occurs at 00:00. That is not always the case. Some dates in some time zones may start at another time. So let java.time determine the first moment of the day.
ZonedDateTime firstMomentOfThe7thInLosAngeles = ld.atStartOfDay( zLosAngeles ) ;
firstMomentOfThe7thInLosAngeles.toString(): 2021-11-07T00:00-07:00[America/Los_Angeles]
But then you jumped to another moment, to 1 AM.
ZonedDateTime oneAmOnThe7thLosAngeles = firstMomentOfThe7thInLosAngeles.with( LocalTime.of( 1 , 0 ) ) ;
oneAmOnThe7thLosAngeles.toString(): 2021-11-07T01:00-07:00[America/Los_Angeles]
That time-of-day may or may not exist on that date in that zone. The ZonedDateTime class will adjust if need be.
You used the name midnightPDT for a variable. I suggest avoiding the term midnight as its use confuses date-time handling with out a precise definition. I recommend using the term "first moment of the day" if that is what you mean.
You extract a count of milliseconds since the epoch reference of first moment of 1970 as seen in UTC, 1970-01-01T00:00Z.
Instant firstMomentOfThe7thInLosAngelesAsSeenInUtc = firstMomentOfThe7thInLosAngeles.toInstant() ;
long millisSinceEpoch_FirstMomentOf7thLosAngeles = firstMomentOfThe7thInLosAngelesAsSeenInUtc.toEpochMilli() ;
firstMomentOfThe7thInLosAngelesAsSeenInUtc.toString(): 2021-11-07T07:00:00Z
millisSinceEpoch_FirstMomentOf7thLosAngeles = 1636268400000
And you do the same for our 1 AM moment.
Instant oneAmOnThe7thLosAngelesAsSeenInUtc = oneAmOnThe7thLosAngeles.toInstant() ;
long millisSinceEpoch_OneAmOn7thLosAngeles = oneAmOnThe7thLosAngelesAsSeenInUtc.toEpochMilli() ;
oneAmOnThe7thLosAngelesAsSeenInUtc.toString(): 2021-11-07T08:00:00Z
millisSinceEpoch_OneAmOn7thLosAngeles = 1636272000000
We should see a difference of one hour. An hour = 3,600,000 = 60 * 60 * 1,000.
long diff = ( millisSinceEpoch_OneAmOn7thLosAngeles - millisSinceEpoch_FirstMomentOf7thLosAngeles ); // 3,600,000 = 60 * 60 * 1,000.
diff = 3600000
Cutover
Then you go on to mention the Daylight Saving Time (DST) cutover. The cutover for DST in the United States on that date was 2 AM, not 1 AM. At the moment of 2 AM arriving, the clocks swung back to 1 AM, for a second 1:00-2:00 AM hour.
To get to that point of cutover, let's add an hour.
ZonedDateTime cutover_Addition = oneAmOnThe7thLosAngeles.plusHours( 1 );
cutover_Addition = 2021-11-07T01:00-08:00[America/Los_Angeles]
Notice that the time-of-day shows the same (1 AM), but the offset-from-UTC has changed from being 7 hours behind UTC to now 8 hours behind UTC. There lies the hour difference you seek.
Let's get the count of milliseconds since epoch for this third moment. Before we had first moment of the day (00:00), then the first occurring 1 AM, and now we have the second occurring 1 AM on this “Fall-Back” date of November 7, 2021.
long millisSinceEpoch_Cutover = cutover_Addition.toInstant().toEpochMilli();
1636275600000
Duration.between( firstMomentOfThe7thInLosAngelesAsSeenInUtc , cutover_Addition.toInstant() ) = PT2H
Duration.between( oneAmOnThe7thLosAngelesAsSeenInUtc , cutover_Addition.toInstant() ) = PT1H
The ZonedDateTime class does offer a pair of methods of use at these moments of cutover: withEarlierOffsetAtOverlap and withLaterOffsetAtOverlap.
ZonedDateTime cutover_OverlapEarlier =
cutover_Addition
.withEarlierOffsetAtOverlap();
ZonedDateTime cutover_OverlapLater =
cutover_Addition
.withLaterOffsetAtOverlap();
cutover_OverlapEarlier = 2021-11-07T01:00-07:00[America/Los_Angeles]
cutover_OverlapLater = 2021-11-07T01:00-08:00[America/Los_Angeles]
Calendar
If you really need a Calendar object, just convert.
Calendar x = GregorianCalendar.from( firstMomentOfThe7thInLosAngeles ) ;
Calendar y = GregorianCalendar.from( oneAmOnThe7thLosAngeles ) ;
Calendar z = GregorianCalendar.from( cutover_Addition );
If you goal is simply struggling with understanding Calendar class behavior, I suggest you stop the masochism. There is no point. Sun, Oracle, and the JCP community all gave up on those terrible legacy date-time classes. I suggest you do the same.
Example code
Pulling together all that code above.
ZoneId zLosAngeles = ZoneId.of( "America/Los_Angeles" );
LocalDate ld = LocalDate.of( 2021 , Month.NOVEMBER , 7 );
ZonedDateTime firstMomentOfThe7thInLosAngeles = ld.atStartOfDay( zLosAngeles );
ZonedDateTime oneAmOnThe7thLosAngeles = firstMomentOfThe7thInLosAngeles.with( LocalTime.of( 1 , 0 ) );
Instant firstMomentOfThe7thInLosAngelesAsSeenInUtc = firstMomentOfThe7thInLosAngeles.toInstant();
long millisSinceEpoch_FirstMomentOf7thLosAngeles = firstMomentOfThe7thInLosAngelesAsSeenInUtc.toEpochMilli();
Instant oneAmOnThe7thLosAngelesAsSeenInUtc = oneAmOnThe7thLosAngeles.toInstant();
long millisSinceEpoch_OneAmOn7thLosAngeles = oneAmOnThe7thLosAngelesAsSeenInUtc.toEpochMilli();
long diff = ( millisSinceEpoch_OneAmOn7thLosAngeles - millisSinceEpoch_FirstMomentOf7thLosAngeles ); // 3,600,000 = 60 * 60 * 1,000.
ZonedDateTime cutover_Addition = oneAmOnThe7thLosAngeles.plusHours( 1 );
long millisSinceEpoch_Cutover = cutover_Addition.toInstant().toEpochMilli();
ZonedDateTime cutover_OverlapEarlier =
cutover_Addition
.withEarlierOffsetAtOverlap();
ZonedDateTime cutover_OverlapLater =
cutover_Addition
.withLaterOffsetAtOverlap();
Convert to legacy classes, if need be.
Calendar x = GregorianCalendar.from( firstMomentOfThe7thInLosAngeles );
Calendar y = GregorianCalendar.from( oneAmOnThe7thLosAngeles );
Calendar z = GregorianCalendar.from( cutover_Addition );
Dump to console.
System.out.println( "firstMomentOfThe7thInLosAngeles = " + firstMomentOfThe7thInLosAngeles );
System.out.println( "oneAmOnThe7thLosAngeles = " + oneAmOnThe7thLosAngeles );
System.out.println( "firstMomentOfThe7thInLosAngelesAsSeenInUtc = " + firstMomentOfThe7thInLosAngelesAsSeenInUtc );
System.out.println( "millisSinceEpoch_FirstMomentOf7thLosAngeles = " + millisSinceEpoch_FirstMomentOf7thLosAngeles );
System.out.println( "oneAmOnThe7thLosAngelesAsSeenInUtc = " + oneAmOnThe7thLosAngelesAsSeenInUtc );
System.out.println( "millisSinceEpoch_OneAmOn7thLosAngeles = " + millisSinceEpoch_OneAmOn7thLosAngeles );
System.out.println( "diff = " + diff );
System.out.println( "x = " + x );
System.out.println( "y = " + y );
System.out.println( "z = " + z );
System.out.println( "cutover_Addition = " + cutover_Addition );
System.out.println( "millisSinceEpoch_Cutover = " + millisSinceEpoch_Cutover );
System.out.println( "Duration.between( firstMomentOfThe7thInLosAngelesAsSeenInUtc , cutover_Addition.toInstant() ) = " + Duration.between( firstMomentOfThe7thInLosAngelesAsSeenInUtc , cutover_Addition.toInstant() ) );
System.out.println( "Duration.between( oneAmOnThe7thLosAngelesAsSeenInUtc , cutover_Addition.toInstant() ) = " + Duration.between( oneAmOnThe7thLosAngelesAsSeenInUtc , cutover_Addition.toInstant() ) );
System.out.println( "cutover_OverlapEarlier = " + cutover_OverlapEarlier );
System.out.println( "cutover_OverlapLater = " + cutover_OverlapLater );
When run.
firstMomentOfThe7thInLosAngeles = 2021-11-07T00:00-07:00[America/Los_Angeles]
oneAmOnThe7thLosAngeles = 2021-11-07T01:00-07:00[America/Los_Angeles]
firstMomentOfThe7thInLosAngelesAsSeenInUtc = 2021-11-07T07:00:00Z
millisSinceEpoch_FirstMomentOf7thLosAngeles = 1636268400000
oneAmOnThe7thLosAngelesAsSeenInUtc = 2021-11-07T08:00:00Z
millisSinceEpoch_OneAmOn7thLosAngeles = 1636272000000
diff = 3600000
x = java.util.GregorianCalendar[time=1636268400000,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=2,minimalDaysInFirstWeek=4,ERA=1,YEAR=2021,MONTH=10,WEEK_OF_YEAR=44,WEEK_OF_MONTH=1,DAY_OF_MONTH=7,DAY_OF_YEAR=311,DAY_OF_WEEK=1,DAY_OF_WEEK_IN_MONTH=1,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=0,SECOND=0,MILLISECOND=0,ZONE_OFFSET=-28800000,DST_OFFSET=3600000]
y = java.util.GregorianCalendar[time=1636272000000,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=2,minimalDaysInFirstWeek=4,ERA=1,YEAR=2021,MONTH=10,WEEK_OF_YEAR=44,WEEK_OF_MONTH=1,DAY_OF_MONTH=7,DAY_OF_YEAR=311,DAY_OF_WEEK=1,DAY_OF_WEEK_IN_MONTH=1,AM_PM=0,HOUR=1,HOUR_OF_DAY=1,MINUTE=0,SECOND=0,MILLISECOND=0,ZONE_OFFSET=-28800000,DST_OFFSET=3600000]
z = java.util.GregorianCalendar[time=1636275600000,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=2,minimalDaysInFirstWeek=4,ERA=1,YEAR=2021,MONTH=10,WEEK_OF_YEAR=44,WEEK_OF_MONTH=1,DAY_OF_MONTH=7,DAY_OF_YEAR=311,DAY_OF_WEEK=1,DAY_OF_WEEK_IN_MONTH=1,AM_PM=0,HOUR=1,HOUR_OF_DAY=1,MINUTE=0,SECOND=0,MILLISECOND=0,ZONE_OFFSET=-28800000,DST_OFFSET=0]
cutover_Addition = 2021-11-07T01:00-08:00[America/Los_Angeles]
millisSinceEpoch_Cutover = 1636275600000
Duration.between( firstMomentOfThe7thInLosAngelesAsSeenInUtc , cutover_Addition.toInstant() ) = PT2H
Duration.between( oneAmOnThe7thLosAngelesAsSeenInUtc , cutover_Addition.toInstant() ) = PT1H
cutover_OverlapEarlier = 2021-11-07T01:00-07:00[America/Los_Angeles]
cutover_OverlapLater = 2021-11-07T01:00-08:00[America/Los_Angeles]
java.time
Is this expected behavior? No. I consider it a bug.
Is there a way to prevent this? Yes, the way you already mentioned or at least implied: use ZonedDateTime instead of Calendar. Basil Bourque has said it already. As a modest supplement I wanted to show the full round-trip from Calendar to ZonedDateTime, setting minute to 0 and converting back to Calendar. In case you need this for interoperability with your legacy code.
GregorianCalendar oneAmPdt = new GregorianCalendar(TimeZone.getTimeZone(ZoneId.of("America/Los_Angeles")));
oneAmPdt.clear();
oneAmPdt.set(2021, Calendar.NOVEMBER, 7, 0, 0);
oneAmPdt.add(Calendar.HOUR_OF_DAY, 1);
System.out.println(oneAmPdt.getTime());
ZonedDateTime zdt = oneAmPdt.toZonedDateTime();
// Minute is already 0 so no change should occur... RIGHT!?
zdt = zdt.withMinute(0);
oneAmPdt = GregorianCalendar.from(zdt);
System.out.println(oneAmPdt.getTime());
Output:
Sun Nov 07 01:00:00 PDT 2021
Sun Nov 07 01:00:00 PDT 2021
But I used GregorianCalendar, not Calendar? So did you. GregorianCalendar is the subclass of Calendar that you got from Calendar.getIntance(). In some environments you would have got a different subclass reflecting the calendar system in use there, and your initial calls to set would not have given you your expected result. You want a GregorianCalendar in this case (if you cannot have a ZonedDateTime from the outset).
When modifying our old code I would likely do it in the above way even if it wasn’t for circumventing a bug in the old Calendar or GregorianCalendar class. It’s one small step in a long-running transition to java.time.

Why the time of AWST and CCT are not the same?

I have write a static class named"DateUtils" and here is a static method named "parseDate(String)" it will use some patterns to convert the string to a date.
the default timeZone is Asia/Shanghai (I'am in China)
public static final TimeZone SHA = TimeZone.getTimeZone("Asia/Shanghai");
and it will be passed to the method
TimeZone.setDefault(timeZone);
And Using SimpleDateFormat to concert the String.
The matched pattern should be "EEE, dd MMM yyyy HH:mm:ss zzz"
And Here are three Test of it.
//this one is ok.
#Test
public void testParseGMTDate() throws Exception {
Date date = DateUtils.parseDate("Thu, 02 Aug 2016 08:12:34 GMT");
assertNotNull(date);
Calendar cal = Calendar.getInstance();
cal.setTime(date);
log.debug(DateUtils.format(cal.getTime()));
assertEquals(cal.get(Calendar.YEAR), 2016);
assertEquals(cal.get(Calendar.MONTH), 8 - 1);
assertEquals(cal.get(Calendar.DATE), 2);
assertEquals(cal.get(Calendar.HOUR_OF_DAY), 8 + 8);
assertEquals(cal.get(Calendar.MINUTE), 12);
assertEquals(cal.get(Calendar.SECOND), 34);
assertEquals(DateUtils.format(cal.getTime()), "2016-08-02 16:12:34");
}
// AWST is GMS+8:00 time so this one is ok.
#Test
public void testParseWSTDate() throws Exception {
Date date = DateUtils.parseDate("Thu, 02 Aug 2016 08:12:34 AWST");
assertNotNull(date);
Calendar cal = Calendar.getInstance();
cal.setTime(date);
log.debug(DateUtils.format(cal.getTime()));
assertEquals(cal.get(Calendar.YEAR), 2016);
assertEquals(cal.get(Calendar.MONTH), 8 - 1);
assertEquals(cal.get(Calendar.DATE), 2);
assertEquals(cal.get(Calendar.HOUR_OF_DAY), 8);
assertEquals(cal.get(Calendar.MINUTE), 12);
assertEquals(cal.get(Calendar.SECOND), 34);
}
// junit.framework.AssertionFailedError:
// Expected :9
// Actual :8
#Test
public void testParseDateCCT() throws Exception {
Date date = DateUtils.parseDate("Thu, 02 Aug 2016 08:12:34 CCT");
assertNotNull(date);
Calendar cal = Calendar.getInstance();
cal.setTime(date);
log.debug(DateUtils.format(cal.getTime()));
assertEquals(cal.get(Calendar.YEAR), 2016);
assertEquals(cal.get(Calendar.MONTH), 8 - 1);
assertEquals(cal.get(Calendar.DATE), 2);
assertEquals(cal.get(Calendar.HOUR_OF_DAY), 8); //Expected: 9
assertEquals(cal.get(Calendar.MINUTE), 12);
assertEquals(cal.get(Calendar.SECOND), 34);
}
And here are some code snippet of DateUtils.
public static final TimeZone SHA = TimeZone.getTimeZone("Asia/Shanghai");
static {
Calendar calendar = Calendar.getInstance();
calendar.setTimeZone(SHA);
calendar.set(2000, 0, 1, 0, 0, 0);
calendar.set(MILLISECOND, 0);
TWO_DIGIT_START = calendar.getTime();
}
public static Date parseDate(String dateValue) {
return parseDate(dateValue, null, null, SHA);
}
public static Date parseDate(String dateValue, String[] dateFormats, Date startDate, TimeZone timeZone) {
TimeZone.setDefault(timeZone);
String[] localDateFormats = dateFormats != null ? dateFormats : DEFAULT_PATTERNS;
Date localStartDate = startDate != null ? startDate : TWO_DIGIT_START;
String v = dateValue;
if (dateValue.length() > 1 && dateValue.startsWith("\'") && dateValue.endsWith("\'")) {
v = dateValue.substring(1, dateValue.length() - 1);
}
String[] arr = localDateFormats;
int len = localDateFormats.length;
for (String dateFormat : DEFAULT_PATTERNS) {
// String dateFormat = arr[i];
SimpleDateFormat dateParser = DateUtils.DateFormatHolder.formatFor(dateFormat);
dateParser.set2DigitYearStart(localStartDate);
ParsePosition pos = new ParsePosition(0);
Date result = dateParser.parse(v, pos);
if (pos.getIndex() != 0) {
_LOG.debug("Date parsed using: {}", dateFormat);
return result;
}
}
_LOG.error("Can't parse data: {data:{}, formats:{}, startDate:{},tz:{}}",
dateValue, localDateFormats, localStartDate, TimeZone.getDefault());
return null;
}
My Question is:
According to document on the internet, CCT & AWST are both the GMT+8:00 time zone, why in my test it seem like that the CCT Time is GMT+9:00.
tl;dr
You have made three mistakes:
Used non-standard non-unique pseudo-time-zone 3-4 letter abbreviations
Then proceeded to incorrectly assume their meaning
Used old outmoded Java classes for date-time handling.
Instead, use proper time zone names in the format of continent/region. Study the documented meaning of those zones rather than assume/guess. Use only java.time classes for date-time work.
All these are common errors made by many programmers, as evidenced by the many Questions here on Stack Overflow.
Use proper time zone names
CST is also Central Standard Time in North America. One example of why you should never use these 3-4 letter abbreviations. They are not true time zones, not standardized, and not even unique(!). Use proper IANA time zone names. These are in the format of continent/region.
java.time
You are using old troublesome date-time classes. Avoid them.
The java.time framework is built into Java 8 and later. These classes supplant the old troublesome date-time classes such as java.util.Date, .Calendar, & java.text.SimpleDateFormat. The Joda-Time team also advises migration to java.time.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations.
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP.
Instant
The Instant class is a moment on the timeline in UTC (GMT) with a resolution up to nanoseconds.
Instant instant = Instant.parse ( "2016-08-02T08:12:34Z" ); // 02 Aug 2016 08:12:34
ZonedDateTime
The ZonedDateTime represents an Instant adjusted into a time zone.
I assume you meant Australian western standard time by AWST, officiall named Australia/Perth.
ZoneId zoneId_Perth = ZoneId.of ( "Australia/Perth" );
ZonedDateTime zdt_Perth = instant.atZone ( zoneId_Perth );
For China, you intended Asia/Shanghai.
ZoneId zoneId_Shanghai = ZoneId.of ( "Asia/Shanghai" );
ZonedDateTime zdt_Shanghai = zdt_Perth.withZoneSameInstant ( zoneId_Shanghai );
I am not sure what your or the old date-time classes meant by CCT. This page says it is “Cocos Islands Time”, six and a half hours ahead of UTC year-round (no Daylight Saving Time, DST). Again, this is an example of why you should never use the 3-4 letter abbreviations.
ZoneId zoneId_Cocos = ZoneId.of ( "Indian/Cocos" );
ZonedDateTime zdt_Cocos = zdt_Perth.withZoneSameInstant ( zoneId_Cocos );
You may have meant Beijing time, according to a comment. If so, know that Beijing time is covered by the Asia/Shanghai time zone according to this list in Wikipedia.
Dump to console. You can see that both Perth and Shanghai are eight hours ahead of UTC in August this year for an hour-of-day of 16 versus 8 (16:12:34 versus 08:12:34). The Cocos Islands is in between, at 14:42:34 for 6.5 hours ahead of UTC.
System.out.println ( "instant: " + instant + " | zdt_Perth: " + zdt_Perth + " | zdt_Shanghai: " + zdt_Shanghai + " | zdt_Cocos: " + zdt_Cocos );
instant: 2016-08-02T08:12:34Z | zdt_Perth: 2016-08-02T16:12:34+08:00[Australia/Perth] | zdt_Shanghai: 2016-08-02T16:12:34+08:00[Asia/Shanghai] | zdt_Cocos: 2016-08-02T14:42:34+06:30[Indian/Cocos]
DateTimeFormatter
To generate strings in formats other than the standard ISO 8601 formats shown here, use DateTimeFormatter class.
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL );
f = f.withLocale( Locale.CHINA );
String output = zdt_Shanghai.format( f );

How do i get the month names in between two dates using Java

I'm trying to find out the month names from 01/01/2014 to 12/28/2014 in the format of "MMM, YY". But i'm getting the below output.
DEC, 14.
JAN, 14.
FEB, 14.
MAR, 14.
APR, 14.
MAY, 14.
JUN, 14.
JUL, 14.
AUG, 14.
SEP, 14.
OCT, 14.
NOV, 14.
I Need an output like this
Jan, 14
.......
.......
Dec, 14
Can anyone help on this.
Here is my code.
String date1 = "01/01/2014";
String date2 = "12/28/2014";
final String OLD_FORMAT = "MM/dd/yyyy";
final String NEW_FORMAT = "MMM, YY";
SimpleDateFormat newFormat = new SimpleDateFormat(OLD_FORMAT);
Date fromDate = newFormat.parse(date1);
Date toDate = newFormat.parse(date2);
newFormat.applyPattern(NEW_FORMAT);
String newFromDate = newFormat.format(fromDate);
String newToDate = newFormat.format(toDate);
//System.out.println(newFromDate);
//System.out.println(newToDate);
DateFormat formater = new SimpleDateFormat(NEW_FORMAT);
Calendar beginCalendar = Calendar.getInstance();
Calendar finishCalendar = Calendar.getInstance();
try {
beginCalendar.setTime(formater.parse(newFromDate));
finishCalendar.setTime(formater.parse(newToDate));
} catch (ParseException e) {
e.printStackTrace();
}
String date;
while (beginCalendar.before(finishCalendar)) {
// add one month to date per loop
date = formater.format(beginCalendar.getTime()).toUpperCase();
System.out.println(date);
beginCalendar.add(Calendar.MONTH, 1);
}
Thanks in Advance.
You can try that:
String date1 = "01/01/2014";
String date2 = "12/28/2014";
final String OLD_FORMAT = "MM/dd/yyyy";
final String NEW_FORMAT = "MMM, YY";
SimpleDateFormat newFormat = new SimpleDateFormat(OLD_FORMAT);
DateFormat formatter = new SimpleDateFormat(NEW_FORMAT);
Date fromDate = newFormat.parse(date1);
Date toDate = newFormat.parse(date2);
Calendar beginCalendar = Calendar.getInstance();
Calendar finishCalendar = Calendar.getInstance();
beginCalendar.setTimeInMillis(fromDate.getTime());
finishCalendar.setTimeInMillis(toDate.getTime());
String date;
while (beginCalendar.before(finishCalendar)) {
// add one month to date per loop
date = formatter.format(beginCalendar.getTime());
System.out.println(date);
beginCalendar.add(Calendar.MONTH, 1);
}
java.time
The java.time framework built into Java 8 and later supplants the troublesome old java.util.Date/.Calendar classes. 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.
The new classes include LocalDate for working with a date-only value sans time-of-day and time zone. From there we will use YearMonth objects since you are interested in, well, year and month.
First parse your two strings into a pair of LocalDate instances.
String inputStart = "01/01/2014";
String inputStop = "12/28/2014";
DateTimeFormatter formatterInput = DateTimeFormatter.ofPattern ( "MM/dd/yyyy" );
LocalDate start = LocalDate.parse ( inputStart , formatterInput );
LocalDate stop = LocalDate.parse ( inputStop , formatterInput );
We are going to generate String representations of our YearMonth objects. The default format for the YearMonth::toString method is the standard ISO 8601 format, YYYY-MM. I strongly suggest using these formats whenever practicable. But your Question specified a different format, so we must define a custom formatter pattern.
Note the explicit Locale object, such as Locale.ENGLISH or Locale.CANADA_FRENCH. If omitted your JVM’s current default locale is implicitly applied to localize the month name. The default can change at any moment, during runtime, so I recommend always specifying your desired/expected locale.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern ( "MMM yyyy" , Locale.ENGLISH ); // Or Locale.CANADA_FRENCH etc.
Rather than use System.out.println, let’s collect the String output for each month in a List.
List<String> outputs = new ArrayList<> ();
Instantiate a YearMonth for our starting LocalDate.
YearMonth yearMonth = YearMonth.from ( start );
From that starting yearMonth, loop a sequence of YearMonth objects, generating a String representation of that year-month value. Continue looping until we increment past the year-month of the ending LocalDate. In each loop we increment by calling plusMonths.
// Repeat while the current year-month is *not* after (is before or is equal) the ending year-month.
while ( ! yearMonth.isAfter ( YearMonth.from ( stop ) ) ) {
//String output = yearMonth.toString (); // ISO 8601 format.
String output = yearMonth.format ( formatter );
outputs.add ( output );
// Prepare for next loop.
yearMonth = yearMonth.plusMonths ( 1 );
}
Dump to console.
System.out.println ( "Months from start: " + start + " to stop: " + stop + " is: " + outputs );
Months from start: 2014-01-01 to stop: 2014-12-28 is: [Jan 2014, Feb 2014, Mar 2014, Apr 2014, May 2014, Jun 2014, Jul 2014, Aug 2014, Sep 2014, Oct 2014, Nov 2014, Dec 2014]
I strongly recommend against using two-digit year numbers as output. I have found such abbreviated years causes confusion in a business. But if you insist, adjust the pattern to yy from yyyy.
Tip: Since YearMonth is built into Java 8 and later, I suggest using objects of this class throughout your code if you are doing more with year-month values than just dumping the month names as seen in your Question.
This code could be made more robust. For example, check that the ending LocalDate is indeed isEqual or isAfter (not isBefore) the starting one. You might be able to optimize a bit by holding the result of YearMonth.from ( stop ) in a variable rather than calling repeatedly in the loop, but I would not bother unless you used this code very often – and the JVM might well optimize that for you automatically.

Odd behavior during timezone conversion

I am trying to convert between a date printed out in an EST timezone into a date printed out in GMT/UTC
package com.stefano;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
public class MainEntry {
/**
* #param args
* #throws ParseException
*/
public static void main(String[] args) throws ParseException {
String dateTime = "1307011200"; //12:00PM 01 July 2013
System.out.println("Input -> " + dateTime);
SimpleDateFormat format = new SimpleDateFormat("yyMMddHHmm");
format.setTimeZone(TimeZone.getTimeZone("EST"));
Date date = format.parse(dateTime);
System.out.println("Intermediate -> " + date);
format.setTimeZone(TimeZone.getTimeZone("GMT"));
System.out.println("Output -> " + format.format(date));
}
}
The output it gives is:
Input -> 1307011200
Intermediate -> Mon Jul 01 17:00:00 BST 2013
Output -> 1307011600
Even though the time difference between EST and GMT is always 5, it is somehow getting involved with BST.
I cannot use Joda-Time.
The javadoc of the SimpleDateFormat.parse(String) method refers to the parse(String, ParsePosition) method, that says:
This parsing operation uses the calendar to produce a Date. As a result, the calendar's date-time fields and the TimeZone value may have been overwritten, depending on subclass implementations. Any TimeZone value that has previously been set by a call to setTimeZone may need to be restored for further operations.
According to this you can't use this method to tell the SimpleDateFormat which timezone
the given date is in.
You can fix this method like this:
String dateTime = "1307011200"; // 12:00PM 01 July 2013
dateTime += " EST"; // append the timezone information to the input string
System.out.println("Input -> " + dateTime);
SimpleDateFormat format = new SimpleDateFormat("yyMMddHHmm z"); // tell the formatter to look for the timezone info
Date date = format.parse(dateTime);
System.out.println("Intermediate -> " + date);
format.setTimeZone(TimeZone.getTimeZone("GMT"));
System.out.println("Output -> " + format.format(date));
This will also print the Date object using your local timezone, but it shows a way to parse the dateTime string using a given timezone.
The answer by zovits is correct.
US East Coast Offset
If by EST you mean the east coast of the United States (and parts of Canada), then your statement the time difference between EST and GMT is always 5 is incorrect. With Daylight Saving Time (DST), the offset may be -05:00 or -04:00. Indeed, your specified date-time does have DST in effect.
Avoid 3-4 Letter Time Zone Codes
Those three or four letter time zone codes are neither standardized nor unique. Avoid them. Use proper time zone names, most of which are continent+city.
Comparison To Joda-Time
For comparison, here is some Joda-Time example code. The java.util.Date & .Calendar classes bundled with Java are so notoriously troublesome that every Java programmer should move to either Joda-Time or the new Java 8 java.time package (inspired by Joda-Time, defined by JSR 310).
While a java.util.Date seems to have a time zone but actually does not, note that a Joda-Time DateTime does truly know its own assigned time zone.
Joda-Time uses the ISO 8601 standard for its defaults. You can use other formats as well, as seen with the Montréal example below.
Example Code
String input = "1307011200"; //12:00PM 01 July 2013
DateTimeFormatter formatterSmooshed = DateTimeFormat.forPattern( "yyMMddHHmm" );
DateTimeZone timeZoneNewYork = DateTimeZone.forID( "America/New_York" );
DateTime dateTimeNewYork = formatterSmooshed.withZone( timeZoneNewYork ).parseDateTime( input );
DateTime dateTimeUtc = dateTimeNewYork.withZone( DateTimeZone.UTC );
String outputMontréal = DateTimeFormat.forStyle( "FF" ).withLocale( Locale.CANADA_FRENCH ).print( dateTimeNewYork );
String outputSmooshed = formatterSmooshed.print( dateTimeNewYork ); // Expect same as input.
Dump to console…
System.out.println( "input: " + input );
System.out.println( "dateTimeNewYork: " + dateTimeNewYork );
System.out.println( "dateTimeUtc: " + dateTimeUtc );
System.out.println( "outputMontréal: " + outputMontréal );
System.out.println( "outputSmooshed: " + outputSmooshed );
When run…
input: 1307011200
dateTimeNewYork: 2013-07-01T12:00:00.000-04:00
dateTimeUtc: 2013-07-01T16:00:00.000Z
outputMontréal: lundi 1 juillet 2013 12 h 00 EDT
outputSmooshed: 1307011200

How to convert Date based on String value for TimeZone

I have a Date Object which I need to convert to the logged in user's timezone. The problem is that the timezone is represented in our DB simply as a String value of GMT plus or minus the offset in hours. So for example "GMT" or "GMT-5" for New york time or "GMT+5".
How can I convert my Date Object to the User's time when all I have are String like "GMT-3" or "GMT+5"?
Thanks in advance for any help.
An example should help, but it seems a 1 character ISO 8601 time zone:
String myDate="2001-07-04T12:08:56GMT-3";
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'GMT'X");
if (myDate.indexOf("GMT-") >= myDate.length() -1 -4) {
myDate = myDate.replace("-","-0");
}
if (myDate.indexOf("GMT+") >= myDate.length() -1 -4) {
myDate = myDate.replace("+","+0");
}
System.out.println(format.parse(myDate));
it should work.
the yyyy-MM-dd'T'HH:mm:ss'GMT'X is compliant with iso8601 time zone
myDate = myDate.replace("-","-0"); adjusts the date to your format
Offset ≠ Time Zone
As Jon Skeet said in comment, a time zone is more than just an offset from UTC/GMT. Storing the offset hours (and minutes) is a less-than-optimal strategy for handling date-time in your database/storage.
Joda-Time
The java.util.Date & java.util.Calendar classes are notoriously troublesome. Avoid them. Use Joda-Time. Or, in Java 8, use the new java.time.* package, defined by JSR 310, and inspired by Joda-Time but re-architected.
We can create a DateTimeZone to represent the offset, but as noted this does not make a complete time zone logically.
We can pass a java.util.Date object directly to a Joda-Time DateTime constructor. Along with that we pass a DateTimeZone object. To go the other direction of conversion, call toDate on a DateTime object.
java.util.Date date = new java.util.Date(); // Retrieved from elsewhere. Faked here.
String offsetInput = "GMT-5";
int offsetHours = 0, offsetMinutes = 0;
offsetInput = offsetInput.replace( "GMT", "" ); // Delete 'GMT' characters.
String[] parts = offsetInput.split(":"); // About splitting a string: http://stackoverflow.com/q/3481828/642706
// Handle results of split.
if( parts.length == 0 ) {
// Add some error handling here
}
if ( parts.length >= 1 ) {
offsetHours = Integer.parseInt( parts[0] ); // Retrieve text of first number (zero-based index counting).
}
if ( parts.length >= 2 ) {
offsetMinutes = Integer.parseInt( parts[1] ); // Retrieve text of second number (zero-based index counting).
}
if( parts.length >= 3 ) {
// Add some error handling here
}
DateTimeZone partialTimeZoneWithOnlyOffset = DateTimeZone.forOffsetHoursMinutes( offsetHours, offsetMinutes );
DateTime dateTime = new DateTime( date, partialTimeZoneWithOnlyOffset );
Dump to console…
System.out.println( "date: " + date ); // BEWARE: JVM's default time zone applied in the implicit call to "toString" of a Date. Very misleading.
System.out.println( "partialTimeZoneWithOnlyOffset: " + partialTimeZoneWithOnlyOffset );
System.out.println( "dateTime: " + dateTime );
System.out.println( "dateTime with alternate formatting: " + DateTimeFormat.forStyle( "FF" ).withLocale( Locale.US ).print( dateTime ) );
When run…
date: Sat Feb 08 22:40:57 PST 2014
partialTimeZoneWithOnlyOffset: -05:00
dateTime: 2014-02-09T01:40:57.810-05:00
dateTime with alternate formatting: Sunday, February 9, 2014 1:40:57 AM -05:00

Categories

Resources