package javaxt.utils;
import java.util.*;
import java.time.*;
import java.time.temporal.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;
//******************************************************************************
//** Date Utils
//******************************************************************************
/**
* Used to parse, format, and compute dates
*
******************************************************************************/
public class Date implements Comparable {
private Locale currentLocale = Locale.getDefault();
private java.util.TimeZone timeZone = Calendar.getInstance().getTimeZone();
private java.util.Date currDate;
public static final String INTERVAL_MILLISECONDS = "S";
public static final String INTERVAL_SECONDS = "s";
public static final String INTERVAL_MINUTES = "m";
public static final String INTERVAL_HOURS = "h";
public static final String INTERVAL_DAYS = "d";
public static final String INTERVAL_WEEKS = "w";
public static final String INTERVAL_MONTHS = "m";
public static final String INTERVAL_YEARS = "y";
private static final HashMap timezones = new HashMap<>();
private static final String[] SupportedFormats = new String[] {
"EEE, d MMM yyyy HH:mm:ss z", // Mon, 7 Jun 1976 13:02:09 EST
"EEE, dd MMM yyyy HH:mm:ss z", // Mon, 07 Jun 1976 13:02:09 EST
"EEE, dd MMM yyyy HH:mm:ss", // Mon, 07 Jun 1976 13:02:09
"EEE MMM dd HH:mm:ss z yyyy", // Mon Jun 07 13:02:09 EST 1976
"EEE MMM d HH:mm:ss z yyyy", // Mon Jun 7 13:02:09 EST 1976
"EEE MMM dd HH:mm:ss yyyy", // Mon Jun 07 13:02:09 1976
"EEE MMM d HH:mm:ss yyyy", // Mon Jun 7 13:02:09 1976
"EEE MMM dd yyyy HH:mm:ss z", //"Mon Jun 07 2013 00:00:00 GMT-0500 (Eastern Standard Time)"
"yyyy-MM-dd HH:mm:ss.SSS Z", // 1976-06-07 13:02:36.000 America/New_York
"yyyy-MM-dd HH:mm:ss.SSSZ", // 1976-06-07 01:02:09.000-0500
"yyyy-MM-dd HH:mm:ss.SSS", // 1976-06-07 01:02:09.000
"yyyy-MM-dd HH:mm:ss Z", // 1976-06-07 13:02:36 America/New_York
"yyyy-MM-dd HH:mm:ssZ", // 1976-06-07 13:02:36-0500
"yyyy-MM-dd HH:mm:ss", // 1976-06-07 01:02:09
"yyyy:MM:dd HH:mm:ss", // 1976:06:07 01:02:09 (exif metadata)
"yyyy-MM-dd-HH:mm:ss.SSS", // 1976-06-07-01:02:09.000
"yyyy-MM-dd-HH:mm:ss", // 1976-06-07-01:02:09
//"yyyy-MM-ddTHH:mm:ss.SSS", // 1976-06-07T01:02:09.000
//"yyyy-MM-ddTHH:mm:ss", // 1976-06-07T01:02:09
"dd-MMM-yyyy h:mm:ss a", // 07-Jun-1976 1:02:09 PM
"dd-MMM-yy h:mm:ss a", // 07-Jun-76 1:02:09 PM
//"d-MMM-yy h:mm:ss a", // 7-Jun-76 1:02:09 PM
"yyyy-MM-dd HH:mm Z", // 1976-06-07 13:02 America/New_York"
"yyyy-MM-dd HH:mmZ", // 1976-06-07T13:02-0500
"yyyy-MM-dd HH:mm", // 1976-06-07T13:02
"yyyy-MM-dd", // 1976-06-07
"dd-MMM-yy", // 07-Jun-76
//"d-MMM-yy", // 7-Jun-76
"dd-MMM-yyyy", // 07-Jun-1976
"MMMMMM d, yyyy", // June 7, 1976
"M/d/yy h:mm:ss a", // 6/7/1976 1:02:09 PM
"M/d/yy h:mm a", // 6/7/1976 1:02 PM
"MM/dd/yy HH:mm:ss Z", // 06/07/1976 13:02:09 America/New_York
"MM/dd/yy HH:mm:ss", // 06/07/1976 13:02:09
"MM/dd/yy HH:mm Z", // 06/07/1976 13:02 America/New_York
"MM/dd/yy HH:mm", // 06/07/1976 13:02
"MM/dd/yyyy HH:mm:ss Z", // 06/07/1976 13:02:09 America/New_York
"MM/dd/yyyy HH:mm:ss", // 06/07/1976 13:02:09
"MM/dd/yyyy HH:mm Z", // 06/07/1976 13:02 America/New_York
"MM/dd/yyyy HH:mm", // 06/07/1976 13:02
"M/d/yy", // 6/7/76
"MM/dd/yyyy", // 06/07/1976
"M/d/yyyy", // 6/7/1976
"yyyyMMddHHmmssSSS", // 19760607130200000
"yyyyMMddHHmmss", // 19760607130200
"yyyyMMdd" // 19760607
};
//**************************************************************************
//** Constructor
//**************************************************************************
/** Creates a new instance of this class using the current time
*/
public Date(){
currDate = new java.util.Date();
}
//**************************************************************************
//** Constructor
//**************************************************************************
/** Creates a new instance of this class using a java.util.Date
*/
public Date(java.util.Date date){
if (date==null) throw new IllegalArgumentException("Date is null.");
currDate = date;
}
//**************************************************************************
//** Constructor
//**************************************************************************
/** Creates a new instance of this class using a java.util.Calendar
*/
public Date(Calendar calendar){
if (calendar==null) throw new IllegalArgumentException("Calendar is null.");
currDate = calendar.getTime();
timeZone = calendar.getTimeZone();
}
//**************************************************************************
//** Constructor
//**************************************************************************
/** Creates a new instance of this class using a java.time.LocalDate
*/
public Date(java.time.LocalDate date){
if (date==null) throw new IllegalArgumentException("Date is null.");
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, date.getYear());
cal.set(Calendar.MONTH, date.getMonthValue()-1);
cal.set(Calendar.DAY_OF_MONTH, date.getDayOfMonth());
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
currDate = cal.getTime();
}
//**************************************************************************
//** Constructor
//**************************************************************************
/** Creates a new instance of this class using a timestamp (in milliseconds)
* since 1/1/1970.
*/
public Date(long milliseconds){
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(milliseconds);
currDate = cal.getTime();
}
//**************************************************************************
//** Constructor
//**************************************************************************
/** Creates a new instance of this class using a String representation of a
* date. Supports multiple common date formats.
*/
public Date(String date) throws ParseException {
try{
//Loop through all known date formats and try to convert the string to a date
for (String format : SupportedFormats){
if (format.endsWith("Z")){
//Special Case: Java fails to parse the "T" in strings like
//"1976-06-07T01:02:09.000" and "1976-06-07T13:02-0500"
int idx = date.indexOf("T");
if (idx==10 && format.startsWith("yyyy-MM-dd HH:mm")){
date = date.substring(0, idx) + " " + date.substring(idx+1);
}
if (date.endsWith("Z") && date.length()==format.length()){
//If the date literally ends with the letter "Z", then the
//date is probably referencing "Zulu" timezone (i.e. UTC).
//Example: "1976-06-07 00:00:00Z". Java doesn't understand
//what the "Z" timezone is so we'll replace the "Z" with
//"UTC".
date = date.substring(0, date.length()-1) + "UTC";
}
else{
//Check if the timezone offset is specified in "+/-HH:mm"
//format (e.g. "2018-01-17T01:00:35+07:00"). If so, update
//the timezone offset by removing the colon.
if (date.length()>=format.length()){
int len = format.length()-1;
String tz = date.substring(len);
if (tz.length()==6){
String a = tz.substring(0,1);
if ((a.equals("-") || a.equals("+")) && tz.indexOf(":")==3){
tz = tz.replace(":", "");
date = date.substring(0, len) + tz;
}
}
}
}
}
try{
currDate = parseDate(date, format);
return;
}
catch(ParseException e){
}
}
}
catch(Exception e){
}
//If we're still here, throw an exception
throw new ParseException("Failed to parse date: " + date, 0);
}
//**************************************************************************
//** Constructor
//**************************************************************************
/** Creates a new instance of date using a date string. The format string is
* used to create a SimpleDateFormat to parse the input date string.
*/
public Date(String date, String format) throws ParseException {
currDate = parseDate(date, format);
}
//**************************************************************************
//** setDate
//**************************************************************************
/** Used to update the current date using a date string. The format parameter
* is used to create a SimpleDateFormat to parse the input date string.
*/
public javaxt.utils.Date setDate(String date, String format) throws ParseException {
currDate = parseDate(date, format);
return this;
}
//**************************************************************************
//** setDate
//**************************************************************************
/** Used to update the current date using a predefined java.util.Date
*/
public javaxt.utils.Date setDate(java.util.Date date){
currDate = date;
return this;
}
//**************************************************************************
//** setLocale
//**************************************************************************
/** Used to update the current local
*/
public javaxt.utils.Date setLocale(Locale locale){
this.currentLocale = locale;
return this;
}
//**************************************************************************
//** getLocale
//**************************************************************************
/** Returns the current local
*/
public Locale getLocale(){
return currentLocale;
}
//**************************************************************************
//** parseDate
//**************************************************************************
/** Attempts to convert a String to a Date via the user-supplied Format
*/
private java.util.Date parseDate(String date, String format) throws ParseException {
if (date!=null){
date = date.trim();
if (date.length()==0) date = null;
}
if (date==null) throw new ParseException("Date is null.", 0);
SimpleDateFormat formatter = new SimpleDateFormat(format, currentLocale);
if (timeZone!=null) formatter.setTimeZone(timeZone);
try{
java.util.Date d = formatter.parse(date);
timeZone = formatter.getTimeZone();
return d;
}
catch(java.text.ParseException e){
//Parse the error. If it's a time zone issue, try to resolve it.
int zIndex = format.toUpperCase().indexOf("Z");
if (zIndex>0){
int errorOffset = e.getErrorOffset();
String tz = null;
if (errorOffset < format.length()){
//Check if the parser choked on the timezone format
String ch = format.substring(errorOffset, errorOffset+1);
if (ch.equalsIgnoreCase("Z") && date.length()>errorOffset){
tz = date.substring(errorOffset);
date = date.substring(0, errorOffset-1);
format = format.substring(0, errorOffset-1);
}
}
else if (errorOffset>format.length()){
//Special Case: "Fri Jan 04 2013 00:00:00 GMT-0500 (Eastern Standard Time)"
tz = date.substring(zIndex);
date = date.substring(0, zIndex-1);
format = format.substring(0, zIndex-1);
}
if (tz!=null){
try{
java.util.TimeZone zone = getTimeZone(tz);
if (zone!=null){
timeZone = zone;
formatter = new SimpleDateFormat(format, currentLocale);
formatter.setTimeZone(timeZone);
return formatter.parse(date);
}
}
catch(Exception ex){
}
}
}
throw e;
}
}
//**************************************************************************
//** setTimeZone
//**************************************************************************
/** Used to set the current time zone. The time zone is used when comparing
* and formatting dates.
* @param timeZone Name of the time zone (e.g. "UTC", "EDT", etc.)
* @param preserveTimeStamp Flag used to indicate whether to preserve the
* timestamp when changing time zones. Normally, when updating the timezone,
* the timestamp is updated to the new timezone. For example, if the current
* time is 4PM EST and you wish to switch to UTC, the timestamp would be
* updated to 8PM. The preserveTimeStamp flag allows users to preserve the
* the timestamp so that the timestamp remains fixed at 4PM.
*/
public javaxt.utils.Date setTimeZone(String timeZone, boolean preserveTimeStamp){
return setTimeZone(getTimeZone(timeZone), preserveTimeStamp);
}
//**************************************************************************
//** setTimeZone
//**************************************************************************
/** Used to set the current time zone. The time zone is used when comparing
* and formatting dates.
* @param timeZone Time zone (e.g. "UTC", "EDT", etc.)
* @param preserveTimeStamp Flag used to indicate whether to preserve the
* timestamp when changing time zones. Normally, when updating the timezone,
* the timestamp is updated to the new timezone. For example, if the current
* time is 4PM EST and you wish to switch to UTC, the timestamp would be
* updated to 8PM. The preserveTimeStamp flag allows users to preserve the
* the timestamp so that the timestamp remains fixed at 4PM.
*/
public javaxt.utils.Date setTimeZone(java.util.TimeZone timeZone, boolean preserveTimeStamp){
if (timeZone==null) return this;
if (preserveTimeStamp){
Calendar cal = Calendar.getInstance(timeZone, currentLocale);
cal.set(Calendar.YEAR, this.getYear());
cal.set(Calendar.MONTH, this.getMonth()-1);
cal.set(Calendar.DAY_OF_MONTH, this.getDay());
cal.set(Calendar.HOUR_OF_DAY, this.getHour());
cal.set(Calendar.MINUTE, this.getMinute());
cal.set(Calendar.SECOND, this.getSecond());
cal.set(Calendar.MILLISECOND, this.getMilliSecond());
currDate = cal.getTime();
}
//Do this last! Otherwise the getHour(), getMinute(), etc will be off...
this.timeZone = timeZone;
return this;
}
//**************************************************************************
//** setTimeZone
//**************************************************************************
/** Used to set the current time zone. The time zone is used when comparing
* and formatting dates.
* @param timeZone Name of the time zone (e.g. "UTC", "EST", etc.)
*/
public javaxt.utils.Date setTimeZone(String timeZone){
return setTimeZone(timeZone, false);
}
//**************************************************************************
//** setTimeZone
//**************************************************************************
/** Used to set the current time zone. The time zone is used when comparing
* and formatting dates.
*/
public javaxt.utils.Date setTimeZone(java.util.TimeZone timeZone){
return setTimeZone(timeZone, false);
}
//**************************************************************************
//** getTimeZone
//**************************************************************************
/** Returns the current time zone. The time zone is used when comparing
* and formatting dates.
*/
public java.util.TimeZone getTimeZone(){
return timeZone;
}
public int hashCode(){
return currDate.hashCode();
}
//**************************************************************************
//** toString
//**************************************************************************
/** Returns the current date as a String in the following format:
* "EEE MMM dd HH:mm:ss z yyyy"
*/
public String toString(){
return toString("EEE MMM dd HH:mm:ss z yyyy");
}
//**************************************************************************
//** toString
//**************************************************************************
/** Used to format the current date into a string.
* @param format Pattern used to format the date (e.g. "MM/dd/yyyy hh:mm a",
* "EEE MMM dd HH:mm:ss z yyyy", etc). Please refer to the
* java.text.SimpleDateFormat class for more information.
*/
public String toString(String format){
SimpleDateFormat currFormatter = new SimpleDateFormat(format, currentLocale);
currFormatter.setTimeZone(timeZone==null ? Calendar.getInstance().getTimeZone() : timeZone);
return currFormatter.format(currDate);
}
//**************************************************************************
//** toString
//**************************************************************************
/** Used to format the current date into a string in a given timezone.
* @param timeZone Name of the time zone (e.g. "UTC", "EST", etc.). Note
* that this parameter does not alter the current date in any way. This
* parameter is simply used for the output string. Use the setTimeZone()
* method to change the timezone for the current date.
*/
public String toString(String format, String timeZone){
return this.toString(format, getTimeZone(timeZone));
}
//**************************************************************************
//** format
//**************************************************************************
/** Used to format the current date into a string.
*/
public String toString(String format, java.util.TimeZone timeZone){
SimpleDateFormat currFormatter =
new SimpleDateFormat(format, currentLocale);
if (timeZone!=null) currFormatter.setTimeZone(timeZone);
return currFormatter.format(currDate);
}
//**************************************************************************
//** format
//**************************************************************************
/** Used to format the current date into a string. Same as toString(format).
*/
public String format(String format){
return toString(format);
}
//**************************************************************************
//** toISOString
//**************************************************************************
/** Returns the date in ISO 8601 format (e.g. "2013-01-04T05:00:00.000Z").
* Note that ISO dates are in UTC.
*/
public String toISOString(){
return toString("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "UTC");
}
//**************************************************************************
//** toLong
//**************************************************************************
/** Returns a long integer used to represent the Date in the following
* format: "yyyyMMddHHmmssSSS". The time zone is automatically set to UTC.
* This is useful for perform simple date comparisons and storing dates
* in a database as integers (e.g. SQLite). Here's an example of how to
* go from a date to a long and a long to a date:
javaxt.utils.Date orgDate = new javaxt.utils.Date();
Long l = orgDate.toLong(); //"yyyyMMddHHmmssSSS" formatted long in UTC
javaxt.utils.Date newDate = new javaxt.utils.Date(l+"");
newDate.setTimeZone("UTC", true);
System.out.println(newDate);
* Note that this method is different from the getTime() method which
* returns the number of milliseconds since January 1, 1970, 00:00:00 UTC.
*/
public long toLong(){
Date d = this.clone();
d.setTimeZone("UTC");
return Long.parseLong(d.toString("yyyyMMddHHmmssSSS"));
}
// //**************************************************************************
// //** toInt
// //**************************************************************************
// /** Returns an integer used to represent the Date in the following format:
// * "yyyyMMdd". The time zone is automatically set to UTC. Here's an example
// * of how to go from a date to an int and an int to a date:
//
// javaxt.utils.Date orgDate = new javaxt.utils.Date();
// int i = orgDate.toInt(); //"yyyyMMdd" formatted integer in UTC
// javaxt.utils.Date newDate = new javaxt.utils.Date(i+"");
// newDate.setTimeZone("UTC", true);
// System.out.println(newDate);
//
// */
// public int toInt(){
// Date d = this.clone();
// d.setTimeZone("UTC");
// return Integer.parseInt(d.toString("yyyyMMdd"));
// }
//**************************************************************************
//** clone
//**************************************************************************
/** Creates a copy of this object. Any modifications to the clone, will not
* affect the original.
*/
public Date clone(){
return new Date(getCalendar());
}
//**************************************************************************
//** equals
//**************************************************************************
/** Used to compare dates and determine whether they are equal.
* @param obj Accepts a java.util.Date, a javaxt.utils.Date, or a String.
*/
public boolean equals(Object obj){
if (obj==null) return false;
if (obj instanceof javaxt.utils.Date){
return ((javaxt.utils.Date) obj).getDate().equals(currDate);
}
else if (obj instanceof java.util.Date){
return ((java.util.Date) obj).equals(currDate);
}
else if (obj instanceof String){
try{
return new javaxt.utils.Date((String) obj).equals(currDate);
}
catch(ParseException e){}
}
return false;
}
//**************************************************************************
//** FormatDate
//**************************************************************************
private String FormatDate(java.util.Date date, String OutputFormat){
SimpleDateFormat formatter =
new SimpleDateFormat(OutputFormat, currentLocale);
if (timeZone != null) formatter.setTimeZone(timeZone);
return formatter.format(date);
}
//**************************************************************************
//** compareTo
//**************************************************************************
/** Used to compare dates. Returns the number of intervals between two dates.
* If the given date is in the future, returns a negative value. If the
* given date is in the past, returns a positive value.
* @param units Units of measure (e.g. hours, minutes, seconds, weeks,
* months, years, etc.)
*/
public long compareTo(javaxt.utils.Date date, String units){
return diff(currDate, date.getDate(), units);
}
//**************************************************************************
//** compareTo
//**************************************************************************
/** Used to compare dates. Returns the number of intervals between two dates
* @param units Units of measure (e.g. hours, minutes, seconds, weeks,
* months, years, etc.)
*/
public long compareTo(java.util.Date date, String units){
return diff(currDate, date, units);
}
//**************************************************************************
//** diff
//**************************************************************************
/** Used to compare dates. Returns a long value representing the difference
* between the dates for the given unit of measure. Note that this method
* will "round down" differences between dates.
* @param interval Unit of measure. Supports seconds, minutes, hours, days,
* weeks, months, and years.
*/
private long diff(java.util.Date date1, java.util.Date date2, String interval){
LocalDate s = new Date(date2).getLocalDate();
LocalDate e = new Date(date1).getLocalDate();
double div = 1;
if (interval.equals("S") || interval.toLowerCase().startsWith("sec")){
div = 1000L;
//return ChronoUnit.SECONDS.between(s, e);
}
if (interval.equals("m") || interval.toLowerCase().startsWith("min")){
div = 60L * 1000L;
//return ChronoUnit.MINUTES.between(s, e);
}
if (interval.equals("H") || interval.toLowerCase().startsWith("h")){
div = 60L * 60L * 1000L;
//return ChronoUnit.HOURS.between(s, e);
}
if (interval.equals("d") || interval.toLowerCase().startsWith("d")){
div = 24L * 60L * 60L * 1000L;
//return ChronoUnit.DAYS.between(s, e);
}
if (interval.equals("w") || interval.toLowerCase().startsWith("w")){
div = 7L * 24L * 60L * 60L * 1000L;
//return ChronoUnit.WEEKS.between(s, e);
}
if (interval.equals("M") || interval.toLowerCase().startsWith("mon")){
//div = 30L * 24L * 60L * 60L * 1000L;
return ChronoUnit.MONTHS.between(s, e);
}
if (interval.equals("y") || interval.toLowerCase().startsWith("y")){
//div = 365L * 24L * 60L * 60L * 1000L;
return ChronoUnit.YEARS.between(s, e);
}
long d1 = date1.getTime();
long d2 = date2.getTime();
int i2 = (int)Math.abs((d1 - d2) / div);
if (date2.after(date1)){
i2 = -i2;
}
return i2;
}
//**************************************************************************
//** getMonthsBetween
//**************************************************************************
/** Returns fractional month difference between two dates. This method will
* return whole numbers (1, 2, 3, etc) if the two dates fall on the same
* day of the month (e.g. "2023-03-01" v "2023-04-01" or "2023-03-14" v
* "2023-04-14"). Returns a decimal value less than or equal to 1 (<=1)
* if the dates fall within the same month (e.g. "2024-01-01" v "2024-01-31"
* yields 1.0 and "2024-01-01" v "2024-01-30" yields 0.968). Otherwise,
* returns the number of full months between the two dates plus a
* fractional value (e.g. "2023-01-27" v "2023-02-28" yields 1.0357). The
* decimal value (numbers after the decimal point) represent fractions of a
* month. Roughly speaking, a day is 0.03 of a month.
*
*
* There are some interesting results when comparing dates around the end
* of two different months. Specifically when comparing a longer month to
* shorter month. For example:
*
*
* - "2024-01-31" v "2024-02-29" = 1 month
* - "2023-01-31" v "2023-02-28" = 1 month
* - "2023-12-31" v "2024-02-29" = 2 months
* - "2022-12-31" v "2023-02-29" = 2 months
*
* In these examples we are following semantic rules and are rounding down
* the differences. However, when we compare dates around the end of two
* different months if the start month is shorter, we don't round. Example:
*
* - "2024-04-30" v "2024-05-31" = 1 month, 1 day
* - "2023-02-28" v "2024-02-29" = 12 months, 1 day
*
*
* In these examples you can see that we are following semantic rules
* rather than straight math.
*
*
* Note that you can use the compareTo() method to compare months using the
* Java standard which rounds down the difference between two months and
* does not return fractions.
*
*/
public static double getMonthsBetween(javaxt.utils.Date start, javaxt.utils.Date end) {
return getMonthsBetween(start.getLocalDate(), end.getLocalDate());
}
private static double getMonthsBetween(LocalDate start, LocalDate end) {
//Check if the start date is after the end date. Swap dates as needed
boolean negate = false;
if (start.isAfter(end)){
negate = true;
LocalDate t = start;
start = end;
end = t;
}
//Check if start/end dates fall on the last of the month
boolean startIsLastDayInMonth = start.getDayOfMonth() == start.lengthOfMonth();
boolean endIsLastDayInMonth = end.getDayOfMonth() == end.lengthOfMonth();
//Calulate months between the 2 dates using Java's built-in ChronoUnit
//Note that the ChronoUnit "rounds down" the interval between the dates.
long m = ChronoUnit.MONTHS.between(start, end);
//When the 2 dates fall on the same day in the month, and the dates aren't
//on the last day of the month, simply return the value returned by the
//ChronoUnit class
int startDay = start.getDayOfMonth();
int endDay = end.getDayOfMonth();
if (startDay==endDay){
if (startIsLastDayInMonth && !endIsLastDayInMonth ||
!startIsLastDayInMonth && endIsLastDayInMonth){
//Example: 2024-11-28 2025-02-28
}
else{
return m;
}
}
//If we're still here, compute fractions
double fraction = 0.0;
if (m==0 && start.getMonthValue()==end.getMonthValue()){
//Simply compare the days of the month
fraction = (end.getDayOfMonth()-(start.getDayOfMonth()-1))/(double)end.lengthOfMonth();
}
else{
//Create new end date using the original end date. Adjust the day
//of the month to match the start date. The new date will be either
//before or after the original end date.
int maxDays = LocalDate.of(end.getYear(), end.getMonthValue(), 1).lengthOfMonth();
LocalDate e2 = LocalDate.of(end.getYear(), end.getMonthValue(), Math.min(start.getDayOfMonth(), maxDays));
if (start.getDayOfMonth()>maxDays){
//Create new date a few days after the end of the month
LocalDate d = e2.plusDays(start.getDayOfMonth()-maxDays);
//Calculate months between the start date and the new date
m = ChronoUnit.MONTHS.between(start, d);
//Calculate fraction
if (startIsLastDayInMonth && endIsLastDayInMonth){}
else{
if (!startIsLastDayInMonth){
fraction = -((start.lengthOfMonth()-start.getDayOfMonth())/(double)start.lengthOfMonth());
}
else{
fraction = -(1-((end.getDayOfMonth())/(double)maxDays));
}
}
}
else{
//Calculate months between the start date and the new end date
m = ChronoUnit.MONTHS.between(start, e2);
//Calculate fraction
if (e2.isAfter(end)){
//subtract from e2
int n = e2.getDayOfMonth()-end.getDayOfMonth();
double f = (double)n/(double)end.lengthOfMonth();
if (m==0){
fraction = 1-f;
}
else{
fraction = -f;
}
}
else if (e2.isBefore(end)){
//add from e2
int x = start.getDayOfMonth()==1 ? 1 : 0;
fraction = (end.getDayOfMonth()-(start.getDayOfMonth()-x))/(double)end.lengthOfMonth();
}
}
}
//Add months and fractions
double diff = fraction+(double)m;
//When the 2 dates fall on the the last day of the month, round up
if ((startIsLastDayInMonth && endIsLastDayInMonth) && (start.getMonthValue()>end.getMonthValue())){
diff = Math.round(diff);
}
//Return diff
return negate ? -diff : diff;
}
private static LocalDate addOrSubtractMonths(long amount, LocalDate d){
if (amount==0) return d;
if (d.getDayOfMonth() == d.lengthOfMonth()){
d = d.withDayOfMonth(1);
if (amount>0){
d = d.plusMonths(amount);
}
else{
d = d.minusMonths(-amount);
}
d = d.withDayOfMonth(d.lengthOfMonth());
}
else{
if (amount>0){
d = d.plusMonths(amount);
}
else{
d = d.minusMonths(-amount);
}
}
return d;
}
//**************************************************************************
//** isBefore
//**************************************************************************
/** Returns true if a given date is before the current date
*/
public boolean isBefore(String date) throws ParseException {
return isBefore(new javaxt.utils.Date(date));
}
//**************************************************************************
//** isBefore
//**************************************************************************
/** Returns true if a given date is before the current date
*/
public boolean isBefore(javaxt.utils.Date Date){
if (Date==null) return false;
return currDate.before(Date.getDate());
}
//**************************************************************************
//** isAfter
//**************************************************************************
/** Returns true if a given date is after the current date
*/
public boolean isAfter(String date) throws ParseException {
return isAfter(new javaxt.utils.Date(date));
}
//**************************************************************************
//** isAfter
//**************************************************************************
/** Returns true if a given date is after the current date
*/
public boolean isAfter(javaxt.utils.Date Date){
if (Date==null) return false;
return currDate.after(Date.getDate());
}
//**************************************************************************
//** add
//**************************************************************************
/** Used to update the current date by adding to (or subtracting from) the
* current date. Example:
javaxt.utils.Date date = new javaxt.utils.Date();
System.out.println("Today is: " + date);
date.add(-1, "day");
System.out.println("Yesterday was: " + date);
* @param units Unit of measure (e.g. hours, minutes, seconds, days, weeks,
* months, years, etc.)
*/
public javaxt.utils.Date add(int amount, String units){
Calendar cal = getCalendar();
int div = 0;
if (units.equals("S") || units.toLowerCase().startsWith("ms") || units.toLowerCase().startsWith("mil")){
div = cal.MILLISECOND;
}
else if (units.equals("s") || units.toLowerCase().startsWith("sec")){
div = cal.SECOND;
}
else if(units.equals("m") || units.toLowerCase().startsWith("min")){
div = cal.MINUTE;
}
else if (units.equals("H") || units.toLowerCase().startsWith("h")){
div = cal.HOUR_OF_DAY;
}
else if (units.toLowerCase().startsWith("d")){
div = cal.DAY_OF_YEAR;
}
else if (units.toLowerCase().startsWith("w")){
div = cal.WEEK_OF_YEAR;
}
else if (units.equals("M") || units.toLowerCase().startsWith("mon")){
div = cal.MONTH;
}
else if (units.toLowerCase().startsWith("y")){
div = cal.YEAR;
}
cal.add(div, amount);
currDate = cal.getTime();
return this;
}
//**************************************************************************
//** subtract
//**************************************************************************
/** Used to update the current date by subtracting from the current date.
* @param units Unit of measure (e.g. hours, minutes, seconds, days, weeks,
* months, years, etc.)
*/
public javaxt.utils.Date subtract(int amount, String units){
return add(-amount, units);
}
//**************************************************************************
//** setDate
//**************************************************************************
/** Used to update the year, month and day of the current date.
* @param month Valid range is 1-12
* @param day Valid range is 1-31
*/
public javaxt.utils.Date setDate(int year, int month, int day){
Calendar cal = getCalendar();
cal.set(Calendar.YEAR, year);
cal.set(Calendar.MONTH, month-1);
cal.set(Calendar.DAY_OF_MONTH, day);
currDate = cal.getTime();
return this;
}
//**************************************************************************
//** setTime
//**************************************************************************
/** Used to update the hours, minutes, seconds, and milliseconds of the
* current date.
*/
public javaxt.utils.Date setTime(int hours, int minutes, int seconds, int milliseconds){
Calendar cal = getCalendar();
cal.set(Calendar.HOUR_OF_DAY, hours);
cal.set(Calendar.MINUTE, minutes);
cal.set(Calendar.SECOND, seconds);
cal.set(Calendar.MILLISECOND, milliseconds);
currDate = cal.getTime();
return this;
}
//**************************************************************************
//** getDate
//**************************************************************************
/** Returns the java.utils.Date representation of the current date.
*/
public java.util.Date getDate(){
return currDate;
}
//**************************************************************************
//** getTime
//**************************************************************************
/** Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT
* represented by this Date object.
*/
public long getTime(){
return getCalendar().getTimeInMillis();
}
//**************************************************************************
//** getCalendar
//**************************************************************************
/** Returns the java.utils.Calender representation of the current date.
*/
public Calendar getCalendar(){
Calendar cal = Calendar.getInstance();
cal.setTime(currDate);
if (this.timeZone!=null) cal.setTimeZone(timeZone);
return cal;
}
//**************************************************************************
//** getLocalDate
//**************************************************************************
/** Returns a java.time.LocalDate representation of the current date.
*/
public LocalDate getLocalDate(){
return LocalDate.of(getYear(), getMonth(), getDay());
}
//**************************************************************************
//** getWeekdayName
//**************************************************************************
/** Returns the name of the day of the week. Example: "Monday"
*/
public String getWeekdayName(){
return FormatDate(currDate, "EEEEEE");
}
//**************************************************************************
//** getWeekdayName
//**************************************************************************
/** Returns the name of the month. Example: "January"
*/
public String getMonthName(){
return FormatDate(currDate, "MMMMMM");
}
//**************************************************************************
//** getDayOfWeek
//**************************************************************************
/** Returns the day of the week (1-7) where Monday = 1
*/
public int getDayOfWeek(){
int dayOfWeek = this.getCalendar().get(Calendar.DAY_OF_WEEK) - 1;
if (dayOfWeek == 0) dayOfWeek = 7;
return dayOfWeek;
}
//**************************************************************************
//** getWeekInMonth
//**************************************************************************
/** Returns the week number in a given month. Example: 11/14/2006 = 3
*/
public int getWeekInMonth(){
return Integer.valueOf(FormatDate(currDate, "W")).intValue();
}
//**************************************************************************
//** getDayInYear
//**************************************************************************
/** Returns the day of the year. Example: 11/14/2006 = 318
*/
public int getDayInYear(){
return Integer.valueOf(FormatDate(currDate, "D")).intValue();
}
//**************************************************************************
//** getWeekInYear
//**************************************************************************
/** Returns the week number within a given year. Example: 11/14/2006 = 46
*/
public int getWeekInYear(){
return Integer.valueOf(FormatDate(currDate, "w")).intValue();
}
//**************************************************************************
//** getYear
//**************************************************************************
/** Returns the current year. Example: 11/14/2006 = 2006
*/
public int getYear(){
return Integer.valueOf(FormatDate(currDate, "yyyy")).intValue();
}
//**************************************************************************
//** getMonth
//**************************************************************************
/** Returns the current month. Example: 11/14/2006 = 11
*/
public int getMonth(){
return Integer.valueOf(FormatDate(currDate, "MM")).intValue();
}
//**************************************************************************
//** getDay
//**************************************************************************
/** Returns the current day of the month. Example: 11/14/2006 = 14
*/
public int getDay(){
return Integer.valueOf(FormatDate(currDate, "dd")).intValue();
}
//**************************************************************************
//** getHour
//**************************************************************************
/** Returns the current hour of the day. Example: 12:00 AM = 0, 1:00 PM = 13
*/
public int getHour(){
return Integer.valueOf(FormatDate(currDate, "HH")).intValue();
}
//**************************************************************************
//** getMinute
//**************************************************************************
/** Returns the current minute of the hour. Example: 12:01 = 1
*/
public int getMinute(){
return Integer.valueOf(FormatDate(currDate, "m")).intValue();
}
//**************************************************************************
//** getSecond
//**************************************************************************
/** Returns the current second of the minute. Example: 12:00:01 = 1
*/
public int getSecond(){
return Integer.valueOf(FormatDate(currDate, "s")).intValue();
}
//**************************************************************************
//** getMilliSecond
//**************************************************************************
/** Returns the current millisecond of the second. Example: 12:00:00:01 = 1
*/
public int getMilliSecond(){
return Integer.valueOf(FormatDate(currDate, "S")).intValue();
}
//**************************************************************************
//** hasTimeStamp
//**************************************************************************
/** Returns true if the date has an hour, minute, second, or millisecond
* greater than zero.
*/
public boolean hasTimeStamp(){
int hour = this.getHour();
int min = this.getMinute();
int sec = this.getSecond();
int ms = this.getMilliSecond();
if (hour>0 || min>0 || sec>0 || ms>0) return true;
return false;
}
//**************************************************************************
//** removeTimeStamp
//**************************************************************************
/** Updates the date by removing the timestamp
*/
public javaxt.utils.Date removeTimeStamp(){
currDate = getShortDate().currDate;
return this;
}
private javaxt.utils.Date getShortDate(){
try{
java.util.TimeZone tz = getTimeZone();
javaxt.utils.Date date = new javaxt.utils.Date(toString("MM/dd/yyyy"));
date.setTimeZone(tz, true);
return date;
}
catch(Exception e){
return this; //Should never happen!
}
}
//**************************************************************************
//** compareTo
//**************************************************************************
/** Compares two dates for ordering. Older dates appear first in an ordered
* list like a TreeSet.
*/
public int compareTo(Object obj){
return new DateComparer().compare(this, obj);
}
//**************************************************************************
//** DateComparer Class
//**************************************************************************
/** Used to compare dates.
*/
private static class DateComparer implements java.util.Comparator {
public final int compare(Object a, Object b){
if (a==null || b==null) return -1;
java.util.Date d1 = null;
java.util.Date d2 = null;
if (a instanceof java.util.Date) d1 = ((java.util.Date) a);
else if (a instanceof javaxt.utils.Date) d1 = ((javaxt.utils.Date) a).getDate();
if (b instanceof java.util.Date) d2 = ((java.util.Date) b);
else if (b instanceof javaxt.utils.Date) d2 = ((javaxt.utils.Date) b).getDate();
if (d1==null || d2==null) return -1;
Long x = d1.getTime();
Long y = d2.getTime();
return x.compareTo(y);
}
}
//**************************************************************************
//** sortDates
//**************************************************************************
/** Static method used to sort dates in a list. Older dates appear first in
* the output.
*/
public static java.util.List sortDates(java.util.List dates){
while(dates.contains(null)){
dates.remove(null);
}
java.util.Collections.sort(dates, new DateComparer());
return dates;
}
//**************************************************************************
//** getTimeZone
//**************************************************************************
/** Static method used to return a timezone for a given ID. Unlike the
* java.util.TimeZone.getTimeZone() method, this method will return a null
* if a given ID cannot be understood.
*
* @param timezone The name or ID of a TimeZone. Supports common
* abbreviations such as "EST" or "EDT", full names such as "Eastern
* Standard Time" or "America/New York", and raw GMT offsets such as
* "GMT-8:00".
*/
public static java.util.TimeZone getTimeZone(String timezone){
//Validate timezone name/id
if (timezone==null) return null;
timezone = timezone.trim();
if (timezone.length()==0) return null;
//Update the string
timezone = timezone.toUpperCase();
if (timezone.startsWith("UTC+") || timezone.startsWith("UTC-")){
timezone = "GMT" + timezone.substring(3);
}
else if ((
timezone.startsWith("AMERICA/") || timezone.startsWith("AFRICA/") ||
timezone.startsWith("EUROPE/") || timezone.startsWith("ASIA/") ||
timezone.startsWith("AUSTRALIA/") || timezone.startsWith("PACIFIC/") ||
timezone.startsWith("ATLANTIC/") || timezone.startsWith("INDIAN/")
) && timezone.contains(" ")){
timezone = timezone.replace(" ", "_");
}
//Special case for timezones like "GMT-0500 (Eastern Standard Time)"
if (timezone.startsWith("GMT")){
if (timezone.contains(" ")){
timezone = timezone.substring(0, timezone.indexOf(" ")).trim();
}
if (!timezone.contains(":")){
int x = 3;
String str = timezone.substring(x);
if (str.startsWith("+") || str.startsWith("-")){
str = str.substring(1);
x++;
}
int y = str.indexOf(" ");
if (y==-1) y = str.length();
str = str.substring(0, y);
if (str.length()==4) x = x+2;
else x = -1;
if (x>0){
timezone = timezone.substring(0, x) + ":" + timezone.substring(x);
}
}
}
timezone = timezones.get(timezone);
if (timezone!=null) return java.util.TimeZone.getTimeZone(timezone);
return null;
}
//**************************************************************************
//** TimeZones
//**************************************************************************
/** Returns a hashmap of all known time zones. Includes time zones packaged
* with Java, Microsoft, and a few others.
*/
public static HashMap getTimeZones(){
return timezones;
}
//**************************************************************************
//** Java TimeZones
//**************************************************************************
/** Updates the hashmap of all known time zones with ones found in Java.
*/
static {
timezones.put("EDT","EST5EDT");
timezones.put("CDT","CST6CDT");
timezones.put("MDT","MST7MDT");
timezones.put("PDT","PST8PDT");
for (String id : java.util.TimeZone.getAvailableIDs()){
java.util.TimeZone timezone = java.util.TimeZone.getTimeZone(id);
//Add standard timezone name/ID
timezones.put(id.toUpperCase(), id);
//Add short name
String shortName = timezone.getDisplayName(true, java.util.TimeZone.SHORT);
if (!timezones.containsKey(shortName)) timezones.put(shortName.toUpperCase(), id);
//Add GMT offset
double offset = timezone.getRawOffset();
String str = (offset/(60*60*1000.0))+"";
int h = Integer.parseInt(str.substring(0, str.indexOf(".")));
float m = Float.parseFloat("0." + str.substring(str.indexOf(".")+1))*60;
String gmt =
"GMT" + (h>-1 ? "+" : "-") +
String.format("%02d", (h<0 ? -h : h)) + ":" +
String.format("%02d", Math.round(m));
if (!timezones.containsKey(gmt)) timezones.put(gmt, gmt);
}
}
//**************************************************************************
//** Microsoft TimeZones
//**************************************************************************
/** Updates the hashmap of all known time zones with a list of time of time
* zones included in the Windows 7 and Windows Server 2008 R2 products.
* These time zone values were available as of October 22, 2009, and may
* have changed since then. For a current list of time zones, on a computer
* running an updated version of Windows 7 or Windows Server 2008 R2, use
* the tzutil /l command. Source:
* http://technet.microsoft.com/en-us/library/ff715394%28v=ws.10%29.aspx
*
* Time zone mappings were taken from this source:
* http://code.google.com/p/java-time-zone-list/source/browse/TimeZones/src/TimeZoneList.java
*
*/
static {
String[][] kvp = {
{ "(UTC+13:00) Nuku'alofa", "Tonga Standard Time", "Pacific/Tongatapu" },
{ "(UTC+12:00) Petropavlovsk-Kamchatsky", "Kamchatka Standard Time", "Asia/Kamchatka" },
{ "(UTC+12:00) Fiji, Marshall Is.", "Fiji Standard Time", "Pacific/Fiji" },
{ "(UTC+12:00) Auckland, Wellington", "New Zealand Standard Time", "Pacific/Auckland" },
{ "(UTC+11:00) Magadan, Solomon Is., New Caledonia", "Central Pacific Standard Time", "Pacific/Guadalcanal" },
{ "(UTC+10:00) Vladivostok", "Vladivostok Standard Time", "Asia/Vladivostok" },
{ "(UTC+10:00) Hobart", "Tasmania Standard Time", "Australia/Hobart" },
{ "(UTC+10:00) Guam, Port Moresby", "West Pacific Standard Time", "Pacific/Port_Moresby" },
{ "(UTC+10:00) Canberra, Melbourne, Sydney", "AUS Eastern Standard Time", "Australia/Sydney" },
{ "(UTC+10:00) Brisbane", "E. Australia Standard Time", "Australia/Brisbane" },
{ "(UTC+09:30) Darwin", "AUS Central Standard Time", "Australia/Darwin" },
{ "(UTC+09:30) Adelaide", "Cen. Australia Standard Time", "Australia/Adelaide" },
{ "(UTC+09:00) Yakutsk", "Yakutsk Standard Time", "Asia/Yakutsk" },
{ "(UTC+09:00) Seoul", "Korea Standard Time", "Asia/Seoul" },
{ "(UTC+09:00) Osaka, Sapporo, Tokyo", "Tokyo Standard Time", "Asia/Tokyo" },
{ "(UTC+08:00) Taipei", "Taipei Standard Time", "Asia/Taipei" },
{ "(UTC+08:00) Perth", "W. Australia Standard Time", "Australia/Perth" },
{ "(UTC+08:00) Kuala Lumpur, Singapore", "Singapore Standard Time", "Asia/Singapore" },
{ "(UTC+08:00) Irkutsk, Ulaan Bataar", "North Asia East Standard Time", "Asia/Irkutsk" },
{ "(UTC+08:00) Beijing, Chongqing, Hong Kong, Urumqi", "China Standard Time", "Asia/Shanghai" },
{ "(UTC+07:00) Krasnoyarsk", "North Asia Standard Time", "Asia/Krasnoyarsk" },
{ "(UTC+07:00) Bangkok, Hanoi, Jakarta", "SE Asia Standard Time", "Asia/Bangkok" },
{ "(UTC+06:30) Yangon (Rangoon)", "Myanmar Standard Time", "Asia/Rangoon" },
{ "(UTC+06:00) Astana, Dhaka", "Central Asia Standard Time", "Asia/Almaty" },
{ "(UTC+06:00) Almaty, Novosibirsk", "N. Central Asia Standard Time", "Asia/Novosibirsk" },
{ "(UTC+05:45) Kathmandu", "Nepal Standard Time", "Asia/Katmandu" },
{ "(UTC+05:30) Sri Jayawardenepura", "Sri Lanka Standard Time", "Asia/Colombo" },
{ "(UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi", "India Standard Time", "Asia/Calcutta" },
{ "(UTC+05:00) Tashkent", "West Asia Standard Time", "Asia/Tashkent" },
{ "(UTC+05:00) Islamabad, Karachi", "Pakistan Standard Time", "Asia/Karachi" },
{ "(UTC+05:00) Ekaterinburg", "Ekaterinburg Standard Time", "Asia/Yekaterinburg" },
{ "(UTC+04:30) Kabul", "Afghanistan Standard Time", "Asia/Kabul" },
{ "(UTC+04:00) Yerevan", "Caucasus Standard Time", "Asia/Yerevan" },
{ "(UTC+04:00) Port Louis", "Mauritius Standard Time", "Indian/Mauritius" },
{ "(UTC+04:00) Baku", "Azerbaijan Standard Time", "Asia/Baku" },
{ "(UTC+04:00) Abu Dhabi, Muscat", "Arabian Standard Time", "Asia/Dubai" },
{ "(UTC+03:30) Tehran", "Iran Standard Time", "Asia/Tehran" },
{ "(UTC+03:00) Tbilisi", "Georgian Standard Time", "Asia/Tbilisi" },
{ "(UTC+03:00) Nairobi", "E. Africa Standard Time", "Africa/Nairobi" },
{ "(UTC+03:00) Moscow, St. Petersburg, Volgograd", "Russian Standard Time", "Europe/Moscow" },
{ "(UTC+03:00) Kuwait, Riyadh", "Arab Standard Time", "Asia/Riyadh" },
{ "(UTC+03:00) Baghdad", "Arabic Standard Time", "Asia/Baghdad" },
{ "(UTC+02:00) Windhoek", "Namibia Standard Time", "Africa/Windhoek" },
{ "(UTC+02:00) Minsk", "E. Europe Standard Time", "Europe/Minsk" },
{ "(UTC+02:00) Jerusalem", "Israel Standard Time", "Asia/Jerusalem" },
{ "(UTC+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius", "FLE Standard Time", "Europe/Kiev" },
{ "(UTC+02:00) Harare, Pretoria", "South Africa Standard Time", "Africa/Johannesburg" },
{ "(UTC+02:00) Cairo", "Egypt Standard Time", "Africa/Cairo" },
{ "(UTC+02:00) Beirut", "Middle East Standard Time", "Asia/Beirut" },
{ "(UTC+02:00) Athens, Bucharest, Istanbul", "GTB Standard Time", "Europe/Istanbul" },
{ "(UTC+02:00) Amman", "Jordan Standard Time", "Asia/Amman" },
{ "(UTC+01:00) West Central Africa", "W. Central Africa Standard Time", "Africa/Lagos" },
{ "(UTC+01:00) Sarajevo, Skopje, Warsaw, Zagreb", "Central European Standard Time", "Europe/Warsaw" },
{ "(UTC+01:00) Brussels, Copenhagen, Madrid, Paris", "Romance Standard Time", "Europe/Paris" },
{ "(UTC+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague", "Central Europe Standard Time", "Europe/Budapest" },
{ "(UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna", "W. Europe Standard Time", "Europe/Berlin" },
{ "(UTC) Monrovia, Reykjavik", "Greenwich Standard Time", "Atlantic/Reykjavik" },
{ "(UTC) Dublin, Edinburgh, Lisbon, London", "GMT Standard Time", "Europe/London" },
{ "(UTC) Coordinated Universal Time", "UTC", "UTC" },
{ "(UTC) Casablanca", "Morocco Standard Time", "Africa/Casablanca" },
{ "(UTC-12:00) International Date Line West", "Dateline Standard Time", "Etc/GMT+12" },
{ "(UTC-11:00) Midway Island, Samoa", "Samoa Standard Time", "Pacific/Apia" },
{ "(UTC-10:00) Hawaii", "Hawaiian Standard Time", "Pacific/Honolulu" },
{ "(UTC-09:00) Alaska", "Alaskan Standard Time", "America/Anchorage" },
{ "(UTC-08:00) Tijuana, Baja California", "Pacific Standard Time (Mexico)", "America/Tijuana" },
{ "(UTC-08:00) Pacific Time (US & Canada)", "Pacific Standard Time", "America/Los_Angeles" }, //vs "PST8PDT"
{ "(UTC-07:00) Mountain Time (US & Canada)", "Mountain Standard Time", "America/Denver" }, //vs "MST7MDT"
{ "(UTC-07:00) Chihuahua, La Paz, Mazatlan", "Mountain Standard Time (Mexico)", "America/Chihuahua" },
{ "(UTC-07:00) Arizona", "US Mountain Standard Time", "America/Phoenix" },
{ "(UTC-06:00) Saskatchewan", "Canada Central Standard Time", "America/Regina" },
{ "(UTC-06:00) Guadalajara, Mexico City, Monterrey", "Central Standard Time (Mexico)", "America/Mexico_City" },
{ "(UTC-06:00) Central Time (US & Canada)", "Central Standard Time", "America/Chicago" }, //vs "CST6CDT"
{ "(UTC-06:00) Central America", "Central America Standard Time", "America/Guatemala" },
{ "(UTC-05:00) Indiana (East)", "US Eastern Standard Time", "America/Indianapolis" },
{ "(UTC-05:00) Eastern Time (US & Canada)", "Eastern Standard Time", "America/New_York" }, //vs "EST5EDT"
{ "(UTC-05:00) Bogota, Lima, Quito", "SA Pacific Standard Time", "America/Bogota" },
{ "(UTC-04:30) Caracas", "Venezuela Standard Time", "America/Caracas" },
{ "(UTC-04:00) Santiago", "Pacific SA Standard Time", "America/Santiago" },
{ "(UTC-04:00) Manaus", "Central Brazilian Standard Time", "America/Cuiaba" },
{ "(UTC-04:00) Georgetown, La Paz, San Juan", "SA Western Standard Time", "America/La_Paz" },
{ "(UTC-04:00) Atlantic Time (Canada)", "Atlantic Standard Time", "America/Halifax" },
{ "(UTC-04:00) Asuncion", "Paraguay Standard Time", "America/Asuncion" },
{ "(UTC-03:30) Newfoundland", "Newfoundland Standard Time", "America/St_Johns" },
{ "(UTC-03:00) Montevideo", "Montevideo Standard Time", "America/Montevideo" },
{ "(UTC-03:00) Greenland", "Greenland Standard Time", "America/Godthab" },
{ "(UTC-03:00) Cayenne", "SA Eastern Standard Time", "America/Cayenne" },
{ "(UTC-03:00) Buenos Aires", "Argentina Standard Time", "America/Buenos_Aires" },
{ "(UTC-03:00) Brasilia", "E. South America Standard Time", "America/Sao_Paulo" },
{ "(UTC-02:00) Mid-Atlantic", "Mid-Atlantic Standard Time", "Etc/GMT+2" },
{ "(UTC-01:00) Cape Verde Is.", "Cape Verde Standard Time", "Atlantic/Cape_Verde" },
{ "(UTC-01:00) Azores", "Azores Standard Time", "Atlantic/Azores" }
/*
----------------------
Asia/Ulaanbaatar", "Ulaanbaatar Standard Time
Asia/Damascus", "Syria Standard Time
Etc/GMT", "GMT
Asia/Dhaka", "Bangladesh Standard Time
Etc/GMT-12", "GMT +12
Asia/Magadan", "Magadan Standard Time
Etc/GMT+11", "GMT -11
Etc/GMT+2", "GMT -02
*/
};
for (String[] pair : kvp) {
timezones.put(pair[0].toUpperCase(), pair[2]);
timezones.put(pair[1].toUpperCase(), pair[2]);
String nk = pair[0].toUpperCase().substring(pair[0].indexOf(" ")+1);
if (!timezones.containsKey(nk)) timezones.put(nk, pair[2]);
}
}
}