Search code examples
javadatetimejava-timetimezone-offset

Java GMT display


I've got a problem to solve here and I don't know how?

I have to do this :

ShowCurrentTime.java, gives a program that displays the current time in GMT. Revise the program so that it prompts the user to enter the time zone offset to GMT and displays the time in the specified time zone.

Here is a sample run:

I've done so far the default GMT time, but I have no Idea whatsoever how to do it..

package ex;

import java.util.*;
import java.text.*;

public class GMT {
   public static void main(String[] args) {       
        final Date currentTime = new Date();
        final SimpleDateFormat sdf = new SimpleDateFormat("EEE, MMM d, yyyy hh:mm:ss a z");

        sdf.setTimeZone(TimeZone.getTimeZone("GMT"));

        System.out.println("GMT time: " + sdf.format(currentTime));  
    }
}

Solution

  • Introduction

    I have scrapped my original answer for this new and improved code. I rewrote everything from the ground up.

    Not only can the program set the time zone based on the time zone id, but it can also parse GMT offset hours and minutes. You can even enter partial hours (with or without minutes)! This code is very dynamic, because it uses regular expressions to interpret the GMT offset times. I tried to be as comprehensive as possible.

    The following output was produced by the code written below.

    Current time: Sat, Mar 22, 2014 07:12:22 PM EDT (GMT+00:00)
    Enter a timezone: Africa/Cairo
    Sun, Mar 23, 2014 01:12:22 AM EET (Eastern European Time)
    Continue? (y/n): y
    Enter a timezone: America/Los_Angeles
    Sat, Mar 22, 2014 04:12:22 PM PDT (Pacific Standard Time)
    Continue? (y/n): y
    Enter a timezone: GMT-05:00
    Sat, Mar 22, 2014 06:12:22 PM GMT-05:00 (GMT-05:00)
    Continue? (y/n): y
    Enter a timezone: GMT-4:30
    Sat, Mar 22, 2014 06:42:22 PM GMT-04:30 (GMT-04:30)
    Continue? (y/n): y
    Enter a timezone: 8
    Sun, Mar 23, 2014 07:12:22 AM GMT+08:00 (GMT+08:00)
    Continue? (y/n): y
    Enter a timezone: 11:30
    Sun, Mar 23, 2014 10:42:22 AM GMT+11:30 (GMT+11:30)
    Continue? (y/n): y
    Enter a timezone: FooBar
    [ERROR] Could not parse GMT ID!
    [ERROR] Invalid GMT TimeZone ID!
    Sun, Mar 23, 2014 10:42:22 AM GMT+11:30 (GMT+11:30)
    Continue? (y/n): y
    Enter a timezone: Asia/Tokyo
    Sun, Mar 23, 2014 08:12:22 AM JST (Japan Standard Time)
    Continue? (y/n): n
    Done...
    

    Complete example:

    The code below should do what you have requested and more!

    import java.text.DateFormat;
    import java.text.SimpleDateFormat;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.Date;
    import java.util.LinkedHashSet;
    import java.util.List;
    import java.util.Scanner;
    import java.util.Set;
    import java.util.TimeZone;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    public class TimeZoneFormatter {
        private static final DateFormat DEFAULT_DATE_FORMAT = new SimpleDateFormat("EEE, MMM d, yyyy hh:mm:ss a z");
        private static final TimeZone GMT_TIME_ZONE = TimeZone.getTimeZone("GMT+00:00");
        private static final Pattern GMT_PATTERN = Pattern.compile("(?:GMT)?([+-])?([012]?\\d)(:([0-5]?\\d))?");
        private static final Pattern REMOVE_LEADING_ZEROS = Pattern.compile("^0+(?!$)");
        private static final Set<String> VALID_IDS = new LinkedHashSet<String>(Arrays.asList(TimeZone.getAvailableIDs()));
        private static final boolean DEBUG = true;
    
        private DateFormat dateFormat;
        private TimeZone timeZone;
    
        public TimeZoneFormatter() {
            this(DEFAULT_DATE_FORMAT);
        }
    
        public TimeZoneFormatter(DateFormat dateFormat) {
            this.dateFormat = dateFormat;
            this.timeZone = GMT_TIME_ZONE;
        }
    
        public void setDateFormat(DateFormat dateFormat) {
            this.dateFormat = dateFormat;
        }
    
        public void setTimezone(boolean positive, int hours, int minutes) {
            String timeZoneId = formatGmtId(positive, hours, minutes);
            setTimezone(timeZoneId);
        }
    
        public void setTimezone(String timeZoneId) {
            TimeZone newTimeZone = TimeZone.getTimeZone(timeZoneId);
    
            if (timeZone.getRawOffset() != 0 && newTimeZone.getRawOffset() == 0) {
                System.out.println("[ERROR] Invalid GMT TimeZone ID!");
            } else {
                timeZone = newTimeZone;
                dateFormat.setTimeZone(timeZone);
            }
        }
    
        public DateFormat getDateFormat() {
            return dateFormat;
        }
    
        public String getTimezoneId() {
            return timeZone.getID();
        }
    
        // -------------------------------------------------------------------------
        // Begin internal parsing and formatting methods.
        // -------------------------------------------------------------------------
    
        private String parseGmtTimeZoneId(String id) {
            Matcher m = GMT_PATTERN.matcher(id);
    
            if (m.find()) {
                char sign = m.group(1) != null ? m.group(1).charAt(0) : '+';
                int hours = removeLeadingZeros(m.group(2));
                int minutes = 0;
    
                if (m.group(3) != null) {
                    minutes = removeLeadingZeros(m.group(4));
                }
    
                return formatGmtId(sign, hours, minutes);
            }
    
            if (!VALID_IDS.contains(id)) {
                System.out.println("[ERROR] Could not parse GMT ID!");
            }
    
            return id;
        }
    
        private void setGmtTimezone(String gmtOffset) {
            String timeZoneId = parseGmtTimeZoneId(gmtOffset);
            setTimezone(timeZoneId);
        }
    
        private String formatGmtId(boolean positive, int hours, int minutes) {
            return formatGmtId(positive ? '+' : '-', hours, minutes);
        }
    
        private String formatGmtId(char sign, int hours, int minutes) {
            return String.format("GMT%c%02d:%02d", sign, hours, minutes);
        }
    
        private int removeLeadingZeros(String intStr) {
            return Integer.parseInt(intStr.replaceAll(REMOVE_LEADING_ZEROS.pattern(), ""));
        }
    
        private String formatDate(final Date time) {
            return String.format("%s (%s)", dateFormat.format(time),
                    timeZone.getDisplayName());
        }
    
        // -------------------------------------------------------------------------
        // Begin main method and other static utility methods.
        // -------------------------------------------------------------------------
    
        public static void main(String[] args) {
            if (DEBUG) {
                printAllAvailableTimeZones();
            }
    
            final Date time = new Date();
            TimeZoneFormatter frmt = new TimeZoneFormatter();
            Scanner scan = new Scanner(System.in);
    
            try {
                System.out.printf("Current time: ");
                printDate(time, frmt);
    
                while (true) {
                    System.out.print("Enter a timezone: ");
                    frmt.setGmtTimezone(scan.nextLine());
                    printDate(time, frmt);
    
                    System.out.print("Continue? (y/n): ");
                    String response = scan.nextLine().trim();
                    if ((response.length() < 1 ? 'y' : response.charAt(0)) == 'n') {
                        break;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                scan.close();
            }
    
            System.out.println("Done...");
        }
    
        public static void printDate(final Date date, TimeZoneFormatter formatter) {
            if (date == null) {
                throw new IllegalArgumentException("Please enter a non-null date.");
            }
    
            if (formatter == null) {
                formatter = new TimeZoneFormatter();
            }
    
            System.out.println(formatter.formatDate(date));
        }   
    
        public static void printAllAvailableTimeZones() {
            System.out.println("Available Time Zones:");
            List<TimeZone> timeZones = new ArrayList<TimeZone>();
    
            for (String id : VALID_IDS) {
                timeZones.add(TimeZone.getTimeZone(id));
            }
    
            Collections.sort(timeZones, new Comparator<TimeZone>() {
                @Override
                public int compare(TimeZone t1, TimeZone t2) {
                    int offsetDiff = t1.getRawOffset() - t2.getRawOffset();
    
                    if (offsetDiff == 0) {
                        int nameDiff = t1.getDisplayName().compareTo(t2.getDisplayName());
    
                        if (nameDiff == 0) {
                            return t1.getID().compareTo(t2.getID());
                        } else {
                            return nameDiff;
                        }
                    } else {
                        return offsetDiff;
                    }
                }
            });
    
            for (TimeZone timeZone : timeZones) {
                System.out.printf("%s : %32s : %s%n", getFormattedOffset(timeZone),
                        timeZone.getID(), timeZone.getDisplayName());
            }
    
            System.out.println();
        }
    
        private static String getFormattedOffset(TimeZone timeZone) {
            int offset = timeZone.getRawOffset();
            char sign = offset < 0 ? '-' : '+';
    
            if (offset < 0) {
                offset *= -1;
            }
    
            int hours = offset / 3600000;
            int mins = (offset % 3600000) / 60000;
    
            return String.format("%c%02d:%02d", sign, hours, mins);
        }
    }
    

    Printing TimeZone offsets and display names:

    Here is a quick print-out of all the time zones that are 5 hours behind GMT time. This can be achieved by calling the printAllAvailableTimeZones() method. This is automatically called in DEBUG mode.

    -05:00 :                   America/Bogota : Colombia Time
    -05:00 :                   America/Havana : Cuba Standard Time
    -05:00 :                             Cuba : Cuba Standard Time
    -05:00 :                 America/Atikokan : Eastern Standard Time
    -05:00 :                   America/Cayman : Eastern Standard Time
    -05:00 :            America/Coral_Harbour : Eastern Standard Time
    -05:00 :                  America/Detroit : Eastern Standard Time
    -05:00 :               America/Fort_Wayne : Eastern Standard Time
    -05:00 :               America/Grand_Turk : Eastern Standard Time
    -05:00 :     America/Indiana/Indianapolis : Eastern Standard Time
    -05:00 :          America/Indiana/Marengo : Eastern Standard Time
    -05:00 :       America/Indiana/Petersburg : Eastern Standard Time
    -05:00 :            America/Indiana/Vevay : Eastern Standard Time
    -05:00 :        America/Indiana/Vincennes : Eastern Standard Time
    -05:00 :          America/Indiana/Winamac : Eastern Standard Time
    -05:00 :             America/Indianapolis : Eastern Standard Time
    -05:00 :                  America/Iqaluit : Eastern Standard Time
    -05:00 :                  America/Jamaica : Eastern Standard Time
    -05:00 :      America/Kentucky/Louisville : Eastern Standard Time
    -05:00 :      America/Kentucky/Monticello : Eastern Standard Time
    -05:00 :               America/Louisville : Eastern Standard Time
    -05:00 :                 America/Montreal : Eastern Standard Time
    -05:00 :                   America/Nassau : Eastern Standard Time
    -05:00 :                 America/New_York : Eastern Standard Time
    -05:00 :                  America/Nipigon : Eastern Standard Time
    -05:00 :                   America/Panama : Eastern Standard Time
    -05:00 :              America/Pangnirtung : Eastern Standard Time
    -05:00 :           America/Port-au-Prince : Eastern Standard Time
    -05:00 :              America/Thunder_Bay : Eastern Standard Time
    -05:00 :                  America/Toronto : Eastern Standard Time
    -05:00 :                   Canada/Eastern : Eastern Standard Time
    -05:00 :                              EST : Eastern Standard Time
    -05:00 :                          EST5EDT : Eastern Standard Time
    -05:00 :                              IET : Eastern Standard Time
    -05:00 :                          Jamaica : Eastern Standard Time
    -05:00 :                     SystemV/EST5 : Eastern Standard Time
    -05:00 :                  SystemV/EST5EDT : Eastern Standard Time
    -05:00 :                  US/East-Indiana : Eastern Standard Time
    -05:00 :                       US/Eastern : Eastern Standard Time
    -05:00 :                      US/Michigan : Eastern Standard Time
    -05:00 :                America/Guayaquil : Ecuador Time
    -05:00 :                        Etc/GMT+5 : GMT-05:00
    -05:00 :                     America/Lima : Peru Time
    

    Additional resources:

    The following Oracle tutorial: Time Zone and Offset Classes may assist you with your problem further.

    ZoneId and ZoneOffset

    The Date-Time API provides two classes for specifying a time zone or an offset:

    • ZoneId specifies a time zone identifier and provides rules for converting between an Instant and a LocalDateTime.
    • ZoneOffset specifies a time zone offset from Greenwich/UTC time.