/* * [TZ.java] * * Summary: TZ display TimeZones supported on your computer with their offsets from GMT. * * Copyright: (c) 2004-2017 Roedy Green, Canadian Mind Products, http://mindprod.com * * Licence: This software may be copied and used freely for any purpose but military. * http://mindprod.com/contact/nonmil.html * * Requires: JDK 1.8+ * * Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/ * * Version History: * 1.1 2005-06-30 add stomp bat files * 1.2 2005-07-27 add main method * 1.3 2006-03-05 reformat with IntelliJ and and Javadoc * 1.4 2007-08-08 add default TZ display, convert to JDK 1.5, add time display. * add live local time display. * 1.5 2007-08-08 add utc time display, sort by offset and id. * 1.6 2007-10-09 utc time display, in 24-hour time. * 1.7 2008-04-05 add build to title. * 1.8 2008-04-27 make columns sortable. * 1.9 2008-06-13 flip to JDK 1.6 to allow access to TableRowSorter */ package com.mindprod.tz; import com.mindprod.common18.Build; import com.mindprod.common18.CMPAboutJBox; import com.mindprod.common18.Common18; import com.mindprod.common18.FontFactory; import com.mindprod.common18.HybridJ; import com.mindprod.common18.JEButton; import com.mindprod.common18.VersionCheck; import javax.swing.BorderFactory; import javax.swing.JApplet; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.Timer; import javax.swing.table.AbstractTableModel; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; import javax.swing.table.TableModel; import javax.swing.table.TableRowSorter; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Calendar; import java.util.Comparator; import java.util.Date; import java.util.GregorianCalendar; import java.util.TimeZone; /** * TZ display TimeZones supported on your computer with their offsets from GMT. * * @author Roedy Green, Canadian Mind Products * @version 1.9 2008-06-13 flip to JDK 1.6 to allow access to TableRowSorter * @since 2004 */ @SuppressWarnings( { "FieldCanBeLocal" } ) public final class TZ extends JApplet { /** * height of Applet box in pixels. Does not include surrounding frame. */ private static final int APPLET_HEIGHT = 645; /** * Width of Applet box in pixels. */ private static final int APPLET_WIDTH = 822; private static final int FIRST_COPYRIGHT_YEAR = 2004; /** * undisplayed copyright notice */ @SuppressWarnings( { "UnusedDeclaration" } ) private static final String EMBEDDED_COPYRIGHT = "Copyright: (c) 2004-2017 Roedy Green, Canadian Mind Products, http://mindprod.com"; private static final String RELEASE_DATE = "2008-06-13"; private static final String TITLE_STRING = "TimeZones"; private static final String VERSION_STRING = "1.9"; /** * pale violet blue */ private static final Color BACKGROUND_FOR_APPLET = new Color( 0xd4d4ff ); private static final Color FOREGROUND_FOR_INSTRUCTIONS = new Color( 0x008000 ); private static final Color FOREGROUND_FOR_LABEL = new Color( 0x0000b0 ); /** * for titles */ private static final Color FOREGROUND_FOR_TITLE = new Color( 0xdc143c ); /** * pick noon 2007-06-21 0:00 as a typical summer day that likely will be using DST. */ private static final Date aSummerDay = new GregorianCalendar( 2007, Calendar.JUNE, 21 ).getTime(); /** * pick 2007-06-21 0::00 as a typical winter day that will not be using DST. */ private static final Date aWinterDay = new GregorianCalendar( 2007, Calendar.DECEMBER, 21 ).getTime(); /** * for for titles and About buttons */ private static final Font FONT_FOR_TITLE = FontFactory.build( "Dialog", Font.BOLD, 16 ); /** * for for title second line */ private static final Font FONT_FOR_TITLE2 = FontFactory.build( "Dialog", Font.PLAIN, 14 ); /** * zz=PDT zzzz="pacific Daylight Time" EEEE=dow hh=12 hour */ private static final SimpleDateFormat localTimeDisplay = new SimpleDateFormat( "yyyy-MM-dd hh:mm:ss aa zz : zzzzzz EEEE" ); /** * zz=PDT zzzz="pacific Daylight Time" EEEE=dow HH=24 hour */ private static final SimpleDateFormat utcTimeDisplay = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss zz : zzzzzz EEEE" ); private static final int[] PREFERRED_COLUMN_WIDTH = { 45, 45, 215, 330 }; static { utcTimeDisplay.setTimeZone( TimeZone.getTimeZone( "UTC" ) ); } private JButton about; private JLabel instructions; private JLabel localTimeLabel; private JLabel localTZLabel; private JLabel supportedLabel; private JLabel title; /** * title, second line */ private JLabel title2; private JLabel utcTimeLabel; /** * scroll the JTable of TimeZones */ private JScrollPane scroller; /** * grid of TimeZone data */ private JTable timeZoneTable; private JTextField localTimeField; private JTextField localTZField; private JTextField utcTimeField; /** * timer to help us keep clock up to date. */ private Timer timer; /** * allocate all components */ private void buildComponents() { title = new JLabel( TITLE_STRING + " " + VERSION_STRING ); title.setFont( FONT_FOR_TITLE ); title.setForeground( FOREGROUND_FOR_TITLE ); title2 = new JLabel( "released:" + RELEASE_DATE + " build:" + Build.BUILD_NUMBER ); title2.setFont( FONT_FOR_TITLE2 ); title2.setForeground( FOREGROUND_FOR_TITLE ); about = new JEButton( "About" ); about.setToolTipText( "About " + TITLE_STRING + " " + VERSION_STRING ); about.addActionListener( new ActionListener() { /** * Method actionPerformed ... * @param e of type ActionEvent */ public void actionPerformed( ActionEvent e ) { // open aboutbox frame new CMPAboutJBox( TITLE_STRING, VERSION_STRING, "Display all Timezones that Java supports on this machine.", "", "freeware", RELEASE_DATE, FIRST_COPYRIGHT_YEAR, "Roedy Green", "TZ", "1.8" ); } } ); utcTimeLabel = new JLabel( "UTC" ); utcTimeLabel.setForeground( FOREGROUND_FOR_LABEL ); utcTimeField = new JTextField( localTimeDisplay.format( new Date() ) ); utcTimeField.setEditable( false ); localTimeLabel = new JLabel( "local time" ); localTimeLabel.setForeground( FOREGROUND_FOR_LABEL ); localTimeField = new JTextField( localTimeDisplay.format( new Date() ) ); localTimeField.setEditable( false ); localTZLabel = new JLabel( "local TZ" ); localTZLabel.setForeground( FOREGROUND_FOR_LABEL ); localTZField = new JTextField( TimeZone.getDefault().getDisplayName() ); localTZField.setEditable( false ); supportedLabel = new JLabel( "Supported TimeZones", JLabel.CENTER ); supportedLabel.setForeground( FOREGROUND_FOR_LABEL ); final String[] allTimeZoneIDs = TimeZone.getAvailableIDs(); Arrays.sort( allTimeZoneIDs, new ByOffset() ); final TZTableModel model = new TZTableModel( allTimeZoneIDs ); timeZoneTable = new JTable( model ); // set some initial column widths final TableColumnModel tableColumnModel = timeZoneTable.getColumnModel(); // give a bit more space between columns tableColumnModel.setColumnMargin( 5 ); // hook up our HeaderRenderer to all four columns // and set preferred width in pixels for each column. final TableCellRenderer headerRenderer = new HeaderRenderer(); for ( int i = 0; i < 4; i++ ) { final TableColumn tableColumn = tableColumnModel.getColumn( i ); tableColumn.setHeaderRenderer( headerRenderer ); tableColumn.setPreferredWidth( PREFERRED_COLUMN_WIDTH[ i ] ); } scroller = new JScrollPane( timeZoneTable, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED ); // this is all you need to make every column sortable ascending/descending by clicking on the heading. timeZoneTable.setRowSorter( new TableRowSorter<>( model ) ); instructions = new JLabel( "Click column heading to sort.", JLabel.CENTER ); instructions.setForeground( FOREGROUND_FOR_INSTRUCTIONS ); } private void layoutComponents( Container contentPane ) { // ---0-----1-----2--------3-- // 0 title----- title2-- about // ~time time---------------- // ~default default----------- // ~ supported---------------- // jtable -------------------- // instructions--------------- // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( title, new GridBagConstraints( 0, 0, 2, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets( 10, 10, 5, 5 ), 0, 0 ) ); contentPane.add( title2, new GridBagConstraints( 2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets( 10, 5, 5, 5 ), 0, 0 ) ); contentPane.add( about, new GridBagConstraints( 3, 0, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets( 10, 5, 5, 10 ), 0, 0 ) ); contentPane.add( utcTimeLabel, new GridBagConstraints( 0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets( 10, 10, 0, 5 ), 0, 0 ) ); contentPane.add( utcTimeField, new GridBagConstraints( 1, 1, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets( 10, 5, 0, 10 ), 0, 0 ) ); contentPane.add( localTimeLabel, new GridBagConstraints( 0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets( 10, 10, 0, 5 ), 0, 0 ) ); contentPane.add( localTimeField, new GridBagConstraints( 1, 2, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets( 10, 5, 0, 10 ), 0, 0 ) ); contentPane.add( localTZLabel, new GridBagConstraints( 0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets( 10, 10, 0, 5 ), 0, 0 ) ); contentPane.add( localTZField, new GridBagConstraints( 1, 3, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets( 10, 5, 0, 10 ), 0, 0 ) ); contentPane.add( supportedLabel, new GridBagConstraints( 0, 4, 3, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets( 10, 10, 0, 10 ), 0, 0 ) ); contentPane.add( scroller, new GridBagConstraints( 0, 5, 4, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets( 10, 10, 10, 10 ), 0, 0 ) ); contentPane.add( instructions, new GridBagConstraints( 0, 6, 4, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets( 10, 10, 10, 10 ), 0, 0 ) ); } /** * Allow this Applet to run as as application as well. * * @param args command line arguments ignored. */ public static void main( String args[] ) { HybridJ.fireup( new TZ(), TITLE_STRING + " " + VERSION_STRING, APPLET_WIDTH, APPLET_HEIGHT ); } // end main /** * Called by the browser or Applet viewer to inform * this Applet that it is being reclaimed and that it should destroy * any resources that it has allocated. */ public void destroy() { about = null; instructions = null; localTimeField = null; localTimeLabel = null; localTZField = null; localTZLabel = null; scroller = null; supportedLabel = null; timer = null; timeZoneTable = null; title2 = null; title = null; utcTimeField = null; utcTimeLabel = null; } /** * Called by the browser or Applet viewer to inform * this Applet that it has been loaded into the system. */ @Override public void init() { if ( !VersionCheck.isJavaVersionOK( 1, 8, 0, this ) ) { return; } Common18.setLaf(); Container contentPane = this.getContentPane(); contentPane.setBackground( BACKGROUND_FOR_APPLET ); contentPane.setLayout( new GridBagLayout() ); buildComponents(); layoutComponents( contentPane ); this.validate(); } /** * usual Applet start, we start up Timer to update clock */ public void start() { timer = new Timer( 500/* every half second */, new ActionListener() { public void actionPerformed( ActionEvent e ) { // executed on Swing Thread. localTimeField.setText( localTimeDisplay.format( new Date() ) ); utcTimeField.setText( utcTimeDisplay.format( new Date() ) ); } } ); timer.setRepeats( true ); // start it ticking, timer.start(); } /** * usual Applet stop, we kill Timer to update clock */ public void stop() { timer.stop(); timer = null; } /** * Sort by TimeZone offset. *

* Defines an alternate sort order for String. */ private static class ByOffset implements Comparator { /** * Sort by TimeZone offset. * Defines an alternate sort order for String. * Compare two String Objects. * Compares getRawOffset then String * Informally, returns (a-b), or +ve if a is more positive than b. * * @param a first String to compare * @param b second String to compare * * @return +ve if a>b, 0 if a==b, -ve if a<b */ public final int compare( String a, String b ) { final int diff = TimeZone.getTimeZone( a ).getRawOffset() - TimeZone.getTimeZone( b ).getRawOffset(); if ( diff != 0 ) { return diff; } return a.compareTo( b ); } } /** * nested inner class to render a column heading */ static final class HeaderRenderer implements TableCellRenderer { /** * template for HeaderRender, reuses same component for all headers. */ private static final JLabel headerTemplate; static { headerTemplate = new JLabel( "", JLabel.CENTER ); headerTemplate.setForeground( FOREGROUND_FOR_LABEL ); headerTemplate.setBackground( BACKGROUND_FOR_APPLET ); headerTemplate.setBorder( BorderFactory.createLineBorder( Color.GRAY ) ); headerTemplate.setOpaque( true ); } public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column ) { headerTemplate.setText( value.toString() ); return headerTemplate; } } /** * nested inner class */ static final class TZTableModel extends AbstractTableModel implements TableModel { /** * names of the columns */ static final String[] columnNames = { "Offset", "DST", "ID", "Display Name" }; /** * timeZoneIDs array of all possible TimeZone IDs */ private final String[] timeZoneIDs; /** * constructor. * * @param timeZoneIDs array of all possible TimeZone IDs, sorted in some reasonable order. */ TZTableModel( String[] timeZoneIDs ) { this.timeZoneIDs = timeZoneIDs; } /** * Returns the most specific superclass for all the cell values in the column. This is used by the * JTable to set up a default renderer and editor for the column. * * @param columnIndex the index of the column * * @return the common ancestor class of the object values in the model. */ public Class getColumnClass( int columnIndex ) { switch ( columnIndex ) { case 0: case 1: return Double.class; case 2: case 3: return String.class; default: throw new ArrayIndexOutOfBoundsException( "column index out of range in get ColumnClass" ); } } /** * Returns the number of columns in the model. A JTable uses this method to determine how many * columns it should create and display by default. * * @return the number of columns in the model * @see #getRowCount */ public int getColumnCount() { return 4; } /** * Returns the name of the column at columnIndex. This is used to initialize the table's column * header name. Note: this name does not need to be unique; two columns in a table can have the same name. * * @param column the index of the column * * @return the name of the column */ public String getColumnName( int column ) { return columnNames[ column ]; } /** * Returns the number of rows in the model. A JTable uses this method to determine how many rows it * should display. This method should be quick, as it is called frequently during rendering. * * @return the number of rows in the model * @see #getColumnCount */ public int getRowCount() { return timeZoneIDs.length; } /* get value at given row and column * @param row zero-based row number * @param col zero-based column number * @return Object, will be String, Integer or Float. */ public Object getValueAt( int rowIndex, int columnIndex ) { final String timeZoneID = timeZoneIDs[ rowIndex ]; final TimeZone timeZone = TimeZone.getTimeZone( timeZoneID ); switch ( columnIndex ) { case 0: // offset in hours. // want floating point arithmetic. Don't use TimeUnit. return timeZone.getRawOffset() / ( 60 * 60 * 1000.0D ); case 1: final double raw = timeZone.getRawOffset() / ( 60 * 60 * 1000.0D );// hrs winter final double summer = timeZone.getOffset( aSummerDay.getTime() ) / ( 60 * 60 * 1000.0D );// millis -> hrs summer final double winter = timeZone.getOffset( aWinterDay.getTime() ) / ( 60 * 60 * 1000.0D );// millis -> hrs summer // dst in hours. return ( raw == summer ) ? winter : summer; case 2: return timeZoneID; case 3: return timeZone.getDisplayName(); default: throw new ArrayIndexOutOfBoundsException( "col index out of range in get getValueAt" ); } } } }