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.
So I am using getAvailableZoneIds() method under java.time.ZoneId to fetch a list of available timezones.
I wanted to know if there is a way by which for a specific timezone eg- “America/Chicago” , I can find out the starting and ending date time detail of daylight saving time for a year.
Like when does it start and ends.
Gone though various classes including ZoneId ,ZonedDateTime, ZoneOffset,TimeZone available in java but not able to find any way by which I can fetch this details.
I tried the below code and gives output mentioned below
ZoneId zoneId= ZoneId.of("America/Sao_Paulo");
ZoneRules zoneRules = zoneId.getRules();
System.out.println("previous Transition of DST ==> " + zoneRules.previousTransition(Instant.now()));
System.out.println("next Transition of DST ==> " + zoneRules.nextTransition(Instant.now()));
Output:
previous Transition of DST ==> Transition[Overlap at 2019-02-17T00:00-02:00 to -03:00]
next Transition of DST ==> Transition[Gap at 2019-11-03T00:00-03:00 to -02:00]
But I need to find out for a particular year , what time it starts and what time does it end .
For a particular year, you can give ZoneRules a year as part of specifying a moment. For any given moment, you can ask next or previous ZoneOffsetTransition.
Here is example code for Europe/Sofia time zone.
ZoneId zoneSofia = ZoneId.of( "Europe/Sofia" );
ZoneRules zoneRules = zoneSofia.getRules();
// Pick a moment, arbitrarily.
ZonedDateTime zdt = ZonedDateTime.of( 2019 , 10 , 15 , 10 , 0 , 0 , 0 , zoneSofia );
// Is DST in effect at that moment?
boolean isDst = zoneRules.isDaylightSavings( zdt.toInstant() );
// When are the closest offset transitions, previous (in the past), and next (in the future).
ZoneOffsetTransition previousTransition = zoneRules.previousTransition( zdt.toInstant() );
ZoneOffsetTransition nextTransition = zoneRules.nextTransition( zdt.toInstant() );
// When is the next transition happening in UTC? In Sofia time?
Instant nextTransitionInstant = nextTransition.getInstant(); // An `Instant`` is always in UTC, by definition.
ZonedDateTime nextTransactionZdt = nextTransitionInstant.atZone( zoneSofia ); // Same moment, same point on the timeline, different wall-clock time.
boolean isDstAfterTransition = zoneRules.isDaylightSavings( nextTransactionZdt.toInstant() );
Dump to console.
System.out.println( "zone = " + zoneSofia );
System.out.println( "zdt: " + zdt );
System.out.println( "isDst: " + isDst );
System.out.println( "previousTransition = " + previousTransition );
System.out.println( "nextTransition = " + nextTransition );
System.out.println( "nextTransitionInstant = " + nextTransitionInstant );
System.out.println( "nextTransactionZdt = " + nextTransactionZdt );
System.out.println( "isDstAfterTransition = " + isDstAfterTransition );
zone = Europe/Sofia
zdt: 2019-10-15T10:00+03:00[Europe/Sofia]
isDst: true
previousTransition = Transition[Gap at 2019-03-31T03:00+02:00 to +03:00]
nextTransition = Transition[Overlap at 2019-10-27T04:00+03:00 to +02:00]
nextTransitionInstant = 2019-10-27T01:00:00Z
nextTransactionZdt = 2019-10-27T03:00+02:00[Europe/Sofia]
isDstAfterTransition = false
We can see than the next transition in Europe/Sofia happens at the moment that would look like 4 AM while 3 hours ahead of UTC:
nextTransition = Transition[Overlap at 2019-10-27T04:00+03:00 to +02:00]
…but since we are doing a Daylight Saving Time (DST) “Fall back”, we turn the hands of the clock back to 3 AM to be 2 hours ahead of UTC:
nextTransactionZdt = 2019-10-27T03:00+02:00[Europe/Sofia]
And we can see with isDstAfterTransition that at that moment we are no longer in DST.
Notice how, on this date of the 27th, the people of the the Sofia region experience the hour of 3-4 AM twice. This first 3-4 AM hour is at 3 hours ahead of UTC. The second 3-4 AM hour is at 2 hours ahead of UTC.
And, this means the day of the 27th runs 25 hours long rather than 24.
I am using java 8 time API to convert time in milliseconds to date and I am getting wrong day of week from that date.
here is my code -
long millis = 1406865600000L;
LocalDate localDate = LocalDate.ofEpochDay(millis/1000);
DayOfWeek dow = localDate.getDayOfWeek();
System.out.println(dow);
it prints out THURSDAY, while this is date Friday 1 AUG.
Please help.
LocalDate.ofEpochDay() expects day count as its argument, and you're passing second count (which is what you get when dividing millisecond count by 1000).
The Answer by NPE is correct.
Solution code example
Here is code to handle the situation correctly.
If your input number is a number of milliseconds since the epoch of start of 1970 in UTC, then create an Instant.
Instant instant = Instant.ofEpochMilli( 1406865600000L );
Determining a date requires a time zone. For any given moment, the date varies around the globe by zone. For example, a few minutes after midnight in Paris France is a new day while still being “yesterday” in Montréal Québec.
So adjust your Instant into a ZonedDateTime object by applying your desired/expected time zone.
ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = instant.atZone( z );
Ask for the day-of-week. Java represents each of the seven days of the week in the enum DayOfWeek.
DayOfWeek dow = zdt.getDayOfWeek();
Ask the DayOfWeek object to translate the name of the day of the week to a human language defined in a Locale.
String output = dow.getDisplayName( TextStyle.FULL_STANDALONE , Locale.CANADA_FRENCH );
Dump to console. Results, at least for America/Montreal, are as you expect, Friday 1 AUG.
System.out.println ( "input: " + input + " | instant: " + instant + " | zdt: " + zdt + " | dow: " + dow + " | output: " + output );
input: 1406865600000 | instant: 2014-08-01T04:00:00Z | zdt: 2014-08-01T00:00-04:00[America/Montreal] | dow: FRIDAY | output: vendredi
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
I am working on Calendar project and I am wondering is there any build in function available in class Calendar in android which can change daily basis or weekly basis.
For e.g. I am storing a data in today's date. And I want to repeat that operation on daily basis or weekly basis.
I don't want to use Calendar api.
e.g.
let's say my Calendar instance variable storing date "2014-26-01"
so I want to do something like
final Calendar c = Calendar.getInstance();
for(int i = o ; i <= 30 ; i++){
yy = c.get(Calendar.YEAR);
mm = c.get(Calendar.MONTH);
dd = c.get(Calendar.DAY_OF_MONTH);
Toast.makeText(this,yy+"-"+mm+"-"+dd,Toast.LENGH_SHORT).show();
/** here i want to change the value of `Calendar c` to next day or next week**/
}
You can use the calendar.add() method to increase or decrease the date.
for example:
public void Calendar getTomorrow(){
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DATE,1);
//return the calendar with the date of tomorrow
return calendar;
}
public void Calendar getYesterday(){
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DATE,-1);
//return the calendar with the date of yesterday
return calendar;
}
By the way, the Joda-Time libary offers convenient plusDays, plusWeeks, and plusMonths methods for such calculations.
// java.util.Date dateNow = new java.util.Date();
// Convert a java.util.Date to Joda-Time. Simply pass Date to constructor.
// DateTime now = new DateTime( dateNow, DateTimeZone.forID( "Europe/Paris" ) );
DateTime now = new DateTime( DateTimeZone.forID( "Europe/Paris" ) );
DateTime tomorrow = now.plusDays( 1 );
DateTime nextWeek = now.plusWeeks( 1 );
DateTime firstMomentOfNextWeek = now.plusWeeks( 1 ).withTimeAtStartOfDay();
DateTime nextMonth = now.plusMonths( 1 );
// Convert from Joda-Time back to old outmoded bundled Java class, java.util.Date.
java.util.Date dateNow = now.toDate();
Dump to console…
System.out.println( "now: " + now );
System.out.println( "now in UTC/GMT: " + now.toDateTime( DateTimeZone.UTC ) );
System.out.println( "tomorrow: " + tomorrow );
System.out.println( "nextWeek: " + nextWeek );
System.out.println( "firstMomentOfNextWeek: " + firstMomentOfNextWeek );
System.out.println( "nextMonth: " + nextMonth );
System.out.println( "dateNow: " + dateNow ); // Remember, a j.u.Date lies. The `toString` applies default time zone, but actually a Date has no time zone.
When run…
now: 2014-01-27T00:06:41.982+01:00
now in UTC/GMT: 2014-01-26T23:06:41.982Z
tomorrow: 2014-01-28T00:06:41.982+01:00
nextWeek: 2014-02-03T00:06:41.982+01:00
firstMomentOfNextWeek: 2014-02-03T00:00:00.000+01:00
nextMonth: 2014-02-27T00:06:41.982+01:00
dateNow: Sun Jan 26 15:06:41 PST 2014