problem on java calendar function

2019-07-15 16:49发布

问题:

I declared Calendar and SimpleDateFormat like this:

calendar = Calendar.getInstance(TimeZone.getTimeZone("Malaysia"));
final SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MMMMM.dd hh:mm aaa");

or:

calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT+08:00"));

Then I call this:

sdf.format(calendar.getTime());

but result is not in correct time zone (+8 hours). What could be the problem?

回答1:

Unless you are going to perform Date/Time related calculations, there is no point in instantiating Calendar with given TimeZone. After calling Calendar's getTime() method, you will receive Date object, which is timezone-less either way (GMT based, actually).

What you need to do, is to set TimeZone for formatter instead. And also do not bother with passing your own format, there is a built-in already:

    // get current time
    // you could just as well use Date now = new Date();
    Calendar now = Calendar.getInstance();

    // Locale for formatter
    Locale malaysianLocale = new Locale("ms", "MY");
    // Default date and time format for Malaysia
    DateFormat defaultMalaysianFormatter = DateFormat.getDateTimeInstance(
            DateFormat.DEFAULT, DateFormat.DEFAULT, malaysianLocale);
    // This step is crucial
    TimeZone malaysianTimeZone = TimeZone.getTimeZone("Asia/Kuala_Lumpur");
    defaultMalaysianFormatter.setTimeZone(malaysianTimeZone);

    System.out.println(defaultMalaysianFormatter.format(now.getTime()));

This prints something like 10 Mei 2011 2:30:05 AM, which I believe is your desired result.



回答2:

Time zone id should be set as Asia/Kuala_Lumpur. Date.toString() always returns time string using default time zone. But your default time zone is different.

    Calendar tzCal = Calendar.getInstance(TimeZone.getTimeZone("Asia/Kuala_Lumpur"));
    Calendar cal = Calendar.getInstance();

    cal.set(Calendar.YEAR, tzCal.get(Calendar.YEAR));
    cal.set(Calendar.MONTH, tzCal.get(Calendar.MONTH));
    cal.set(Calendar.DAY_OF_MONTH, tzCal.get(Calendar.DAY_OF_MONTH));
    cal.set(Calendar.HOUR_OF_DAY, tzCal.get(Calendar.HOUR_OF_DAY));
    cal.set(Calendar.MINUTE, tzCal.get(Calendar.MINUTE));
    cal.set(Calendar.SECOND, tzCal.get(Calendar.SECOND));
    cal.set(Calendar.MILLISECOND, tzCal.get(Calendar.MILLISECOND));
    System.out.println("Current Time = " + sdf.format(cal.getTime()));


回答3:

The TimeZone.getTimeZone() call is incorrect. You have to pass a the correct identifier.

EDIT -- You can try to getAvailableIDs() and iterate through them to make sure you have the correct id.



回答4:

If you've read the javadoc of TimeZone carefully, the way to use getTimeZone is: TimeZone.getTimeZone("GMT-8") or TimeZone.getTimeZone("GMT+8")



回答5:

tl;dr

java.time.ZonedDateTime.now(
    ZoneId.of( "Asia/Kuala_Lumpur" ) 
).toString() 

2018-01-23T18:48:32.263+08:00[Asia/Kuala_Lumpur]

Avoid legacy classes

The Question and other Answers use troublesome old date-time classes that are now legacy, supplanted by the java.time classes.

java.time

The modern approach uses java.time classes. Forget all about the terribly confusing Calendar class.

Current moment

First get the current moment in UTC. The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).

Instant instant = Instant.now() ;

Time zone

Adjust into another time zone.

Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter pseudo-zones such as EST or IST as they are not true time zones, not standardized, and not even unique(!).

ZoneId z = ZoneId.of( "Asia/Kuala_Lumpur" ) ; // Or "Asia/Kuching", etc.

Apply the ZoneId to instantiate a ZonedDateTime object. Both the ZonedDateTime and Instant represent the same moment, the very same point on the timeline, but is viewed through a different wall-clock time.

ZonedDateTime zdt = instant.atZone( z ) ;  // Same moment, different wall-clock time.

Offset

If you had only an offset-from-UTC such as +08:00 rather than a known time zone, you would use ZoneOffset to get a OffsetDateTime instead of a ZoneId & ZonedDateTime. But a time zone is always preferable to a mere offset. A zone is a history of offsets used by the people of particular region.

Strings

To generate a string in standard ISO 8601 format, call toString method.

The ZonedDateTime class wisely extends the standard by appending the time zone name in square brackets.

String output = zdt.toString() ;  // YYYY-MM-DDTHH:MM:SS.SSSSSSSSS[tz]

Localize to the user’s preferences. To localize, specify:

  • FormatStyle to determine how long or abbreviated should the string be.
  • Locale to determine (a) the human language for translation of name of day, name of month, and such, and (b) the cultural norms deciding issues of abbreviation, capitalization, punctuation, separators, and such.

    Locale l = Locale.CANADA_FRENCH ; DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ).withLocale( l ); String output = zdt.format( f );

Dump to console.

System.out.println( "instant.toString(): " + instant ) ;
System.out.println( "output: " + output ) ;
System.out.println( "outputLocalized (always Locale.US on IdeOne.com): " + outputLocalized ) ;

See this code run live at IdeOne.com. Note that IdeOne.com overrides any Locale setting to always use Locale.US.

instant.toString(): 2018-01-23T10:48:32.263Z

output: 2018-01-23T18:48:32.263+08:00[Asia/Kuala_Lumpur]

ooutputLocalized (always Locale.US on IdeOne.com): Tuesday, January 23, 2018 6:48:32 PM MYT


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, Java 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 Java SE 7
    • Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
  • Android
    • Later versions of Android bundle implementations of the java.time classes.
    • For earlier Android, the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). 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.