All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ucar.nc2.time.CalendarDateFormatter Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 1998-2018 John Caron and University Corporation for Atmospheric Research/Unidata
 * See LICENSE for license information.
 */

package ucar.nc2.time;

import org.joda.time.Chronology;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import ucar.nc2.units.DateFormatter;

import javax.annotation.concurrent.ThreadSafe;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.StringTokenizer;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Threadsafe static routines for date formatting.
 * Replacement for ucar.nc2.units.DateFormatter
 *
 * @author John
 * @since 7/9/11
 */
@ThreadSafe
public class CalendarDateFormatter {
  // these are thread-safe (yeah!)
  private static DateTimeFormatter isof = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss'Z'").withZoneUTC();
  private static DateTimeFormatter isof_with_millis_of_second = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").withZoneUTC();
  private static DateTimeFormatter dtf = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss'Z'").withZoneUTC();
  
  private static DateTimeFormatter dtf_with_millis_of_second = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.SSS'Z'").withZoneUTC();
  private static DateTimeFormatter df = DateTimeFormat.forPattern("yyyy-MM-dd").withZoneUTC();
  private static DateTimeFormatter df_units = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.SSS 'UTC'").withZoneUTC(); // udunits

  static public String toDateTimeStringISO(CalendarDate cd) {
	
	 if(cd.getDateTime().getMillisOfSecond() == 0)
		 return isof.print( cd.getDateTime() );
	 else
		 return isof_with_millis_of_second.print(cd.getDateTime());
	 
  }

  static public String toDateTimeStringISO(Date d) {
	  return toDateTimeStringISO( CalendarDate.of(d) );
  }

  static public String toDateTimeStringISO(long millisecs) {
 	  return toDateTimeStringISO( CalendarDate.of(millisecs) );
   }

   static public String toDateTimeString(CalendarDate cd) {

	  if(cd.getDateTime().getMillisOfSecond()==0)	  
		  return dtf.print(cd.getDateTime());
	  else
		  return dtf_with_millis_of_second.print(cd.getDateTime());
  }
  
  static public String toDateTimeString(Date date) {
	    return toDateTimeString(CalendarDate.of(date));
  }  

  static public String toDateTimeStringPresent() {
    return dtf.print(new DateTime());
  }

  static public String toDateString(CalendarDate cd) {
    return df.print(cd.getDateTime());
  }

  static public String toDateStringPresent() {
    return df.print(new DateTime());
  }

  /**
   * udunits formatting
   * @param cd  the calendar date
   * @return udunits formated date
   */
  static public String toTimeUnits(CalendarDate cd){
	  return df_units.print(cd.getDateTime());
  }

  static public String toTimeUnits(Date date){
	  return df_units.print(date.getTime());
  }

  static public CalendarDateFormatter factory(CalendarPeriod period) {
    switch (period.getField()) {
      case Year: return new CalendarDateFormatter("yyyy");
      case Month: return new CalendarDateFormatter("yyyy-MM");
      case Day: return new CalendarDateFormatter("yyyy-MM-dd");
      case Hour: return new CalendarDateFormatter("yyyy-MM-ddTHH");
      default: return new CalendarDateFormatter("yyyy-MM-ddTHH:mm:ss");
    }
  }


  /////////////////////////////////////////////////////////////////////////////
  // reading an ISO formatted date

  /**
   * Old version using DateFormatter
   * @param iso ISO 8601 date String
   * @return equivilent Date
   * 
   * @deprecated As of 4.3.10 use {@link #isoStringToDate(String)} instead
   *     
   */
  @Deprecated
  static public Date parseISODate(String iso) {
    DateFormatter df = new DateFormatter();
    return df.getISODate(iso);
  }
  
  /**
   * Convert an ISO formatted String to a CalendarDate.
   * @param calt calendar, may be null for default calendar (Calendar.getDefault())
   * @param iso ISO 8601 date String
     
possible forms for W3C profile of ISO 8601
        Year:
         YYYY (eg 1997)
      Year and month:
         YYYY-MM (eg 1997-07)
      Complete date:
         YYYY-MM-DD (eg 1997-07-16)
      Complete date plus hours and minutes:
         YYYY-MM-DDThh:mmTZD (eg 1997-07-16T19:20+01:00)
      Complete date plus hours, minutes and seconds:
         YYYY-MM-DDThh:mm:ssTZD (eg 1997-07-16T19:20:30+01:00)
      Complete date plus hours, minutes, seconds and a decimal fraction of a second
         YYYY-MM-DDThh:mm:ss.sTZD (eg 1997-07-16T19:20:30.45+01:00)

   where:
        YYYY = four-digit year
        MM   = two-digit month (01=January, etc.)
        DD   = two-digit day of month (01 through 31)
        hh   = two digits of hour (00 through 23) (am/pm NOT allowed)
        mm   = two digits of minute (00 through 59)
        ss   = two digits of second (00 through 59)
        s    = one or more digits representing a decimal fraction of a second
        TZD  = time zone designator (Z or +hh:mm or -hh:mm)
   except:
       You may use a space instead of the 'T'
       The year may be preceeded by a '+' (ignored) or a '-' (makes the date BCE)
       The date part uses a '-' delimiter instead of a fixed number of digits for each field
       The time part uses a ':' delimiter instead of a fixed number of digits for each field
   
* @return CalendarDate using given calendar * @throws IllegalArgumentException if the String is not a valid ISO 8601 date * @see "http://www.w3.org/TR/NOTE-datetime" */ static public CalendarDate isoStringToCalendarDate(Calendar calt, String iso) throws IllegalArgumentException { DateTime dt = parseIsoTimeString(calt, iso); Calendar useCal = Calendar.of(dt.getChronology()); return new CalendarDate(useCal, dt); } /** * Does not handle non-standard Calendars * @param iso iso formatted string * @return Date * @throws IllegalArgumentException * @deprecated use isoStringToCalendarDate */ static public Date isoStringToDate(String iso) throws IllegalArgumentException { CalendarDate dt = isoStringToCalendarDate(null, iso); return dt.toDate(); } // 1 2 3 static public final String isodatePatternString = "([\\+\\-?\\d]+)([ t]([\\.\\:?\\d]*)([ \\+\\-]\\S*)?z?)?$"; // public for testing // private static final String isodatePatternString = "([\\+\\-\\d]+)[ Tt]([\\.\\:\\d]*)([ \\+\\-]\\S*)?z?)?$"; private static final Pattern isodatePattern = Pattern.compile(isodatePatternString); private static DateTime parseIsoTimeString(Calendar calt, String iso) { iso = iso.trim(); iso = iso.toLowerCase(); Matcher m = isodatePattern.matcher(iso); if (!m.matches()) { //System.out.printf("'%s' does not match regexp '%s'%n", dateUnitString, udunitPatternString); throw new IllegalArgumentException(iso + " does not match regexp " + isodatePatternString); } String dateString = m.group(1); String timeString = m.group(3); String zoneString = m.group(4); // Set the defaults for any values that are not specified int year = 0; int month = 1; int day = 1; int hour = 0; int minute = 0; double second = 0.0; try { boolean isMinus = false; if (dateString.startsWith("-")) { isMinus = true; dateString = dateString.substring(1); } else if (dateString.startsWith("+")) { dateString = dateString.substring(1); } if (dateString.contains("-")) { StringTokenizer dateTokenizer = new StringTokenizer(dateString, "-"); if (dateTokenizer.hasMoreTokens()) year = Integer.parseInt(dateTokenizer.nextToken()); if (dateTokenizer.hasMoreTokens()) month = Integer.parseInt(dateTokenizer.nextToken()); if (dateTokenizer.hasMoreTokens()) day = Integer.parseInt(dateTokenizer.nextToken()); } else { int dateLength = dateString.length(); if (dateLength % 2 != 0) { throw new IllegalArgumentException(dateString + " is ambiguous. Cannot parse uneven " + "length date strings when no date delimiter is used."); } else { // dateString length must be even - only four digit year, two digit month, and // two digit day values are allowed when there is no date delimiter. if (dateLength > 3) year = Integer.parseInt(dateString.substring(0, 4)); if (dateLength > 5) month = Integer.parseInt(dateString.substring(4, 6)); if (dateLength > 7) day = Integer.parseInt(dateString.substring(6, 8)); } } // Parse the time if present if (timeString != null && timeString.length() > 0) { if (timeString.contains(":")) { StringTokenizer timeTokenizer = new StringTokenizer(timeString, ":"); if (timeTokenizer.hasMoreTokens()) hour = Integer.parseInt(timeTokenizer.nextToken()); if (timeTokenizer.hasMoreTokens()) minute = Integer.parseInt(timeTokenizer.nextToken()); if (timeTokenizer.hasMoreTokens()) second = Double.parseDouble(timeTokenizer.nextToken()); } else { int timeLengthNoSubseconds = timeString.length(); // possible this contains a seconds value with subseconds (i.e. 25.125 seconds) // since the seconds value can be a Double, let's check check the length of the // time, without the subseconds (if they exist) if (timeString.contains(".")) { timeLengthNoSubseconds = timeString.split("\\.")[0].length(); } // Ok, so this is a little tricky. // First: A udunit date of 1992-10-8t7 is valid. We want to make sure this still work, so // there is a special case of timeString.length() == 1; // // Second: We have the length of the timeString, without // any subseconds. Given that the values for hour, minute, and second must be two // digit numbers, the length of the timeString, without subseconds, should be even. // However, if someone has encoded time as hhms, there is no way we will be able to tell. // So, this is the best we can do to ensure we are parsing the time properly. // Known failure here: hhms will be interpreted as hhmm, but hhms is not following iso, and // a bad idea to use anyway. if (timeLengthNoSubseconds == 1) { hour = Integer.parseInt(timeString); } else if (timeLengthNoSubseconds % 2 != 0) { throw new IllegalArgumentException(timeString + " is ambiguous. Cannot parse uneven " + "length time strings (ignoring subseconds) when no time delimiter is used."); } else { // dateString length must be even - only four digit year, two digit month, and // two digit day values are allowed when there is no date delimiter. if (timeString.length() > 1) hour = Integer.parseInt(timeString.substring(0, 2)); if (timeString.length() > 3) minute = Integer.parseInt(timeString.substring(2, 4)); if (timeString.length() > 5) second = Double.parseDouble(timeString.substring(4)); } } } if (isMinus) year = -year; // kludge to deal with legacy files using year 0. // 10/10/2013 jcaron if ((year == 0) && (calt == Calendar.gregorian)) { calt = Calendar.proleptic_gregorian; } //if (year < -292275054 || year > 292278993) // throw new IllegalArgumentException(" incorrect date specification = " + iso); // Get a DateTime object in this Chronology Chronology cron = Calendar.getChronology(calt); cron = cron.withUTC(); // default is UTC DateTime dt = new DateTime(year, month, day, hour, minute, 0, 0, cron); // Add the seconds dt = dt.plus((long) (1000 * second)); // Parse the time zone if present if (zoneString != null) { zoneString = zoneString.trim(); if (zoneString.length() > 0 && !zoneString.equalsIgnoreCase("Z") && !zoneString.equalsIgnoreCase("UTC") && !zoneString.equalsIgnoreCase("GMT")) { isMinus = false; if (zoneString.startsWith("-")) { isMinus = true; zoneString = zoneString.substring(1); } else if (zoneString.startsWith("+")) { zoneString = zoneString.substring(1); } // allow 01:00, 1:00, 01 or 0100 int hourOffset; int minuteOffset = 0; int posColon = zoneString.indexOf(':'); if (posColon > 0) { String hourS = zoneString.substring(0,posColon); String minS = zoneString.substring(posColon+1); hourOffset = Integer.parseInt(hourS); minuteOffset = Integer.parseInt(minS); } else { // no colon - assume 2 digit hour, optional minutes if (zoneString.length() > 2) { String hourS = zoneString.substring(0,2); String minS = zoneString.substring(2); hourOffset = Integer.parseInt(hourS); minuteOffset = Integer.parseInt(minS); } else { hourOffset = Integer.parseInt(zoneString); } } if (isMinus) { // DateTimeZone.forOffsetHoursMinutes: "If constructed with the values (-2, 30), the resulting zone is '-02:30'. // so i guess dont make minuteOffset negetive hourOffset = -hourOffset; } DateTimeZone dtz = DateTimeZone.forOffsetHoursMinutes(hourOffset, minuteOffset); // Apply the time zone offset, retaining the field values. This // manipulates the millisecond instance. dt = dt.withZoneRetainFields(dtz); // Now convert to the UTC time zone, retaining the millisecond instant dt = dt.withZone(DateTimeZone.UTC); } //else { //dt = dt.withZone(DateTimeZone.UTC); // default UTC //} //} else { // dt = dt.withZone(DateTimeZone.UTC); // default UTC } return dt; } catch (Throwable e) { // catch random joda exceptions throw new IllegalArgumentException("Illegal base time specification: '" + dateString+"' "+e.getMessage()); } } ///////////////////////////////////////////// private final DateTimeFormatter dflocal; /** * Date formatter with specified pattern. * NOTE: we are using jodatime patterns right now, but may switch to jsr-310 when thats available in java 8. * Not sure whether these patterns will still work then, so use this formatter at the risk of having to * change it eventually. OTOH, its likely that the same functionality will be present in jsr-310. *

* The pattern syntax is mostly compatible with java.text.SimpleDateFormat - * time zone names cannot be parsed and a few more symbols are supported. * All ASCII letters are reserved as pattern letters, which are defined as follows: *

*
   * Symbol  Meaning                      Presentation  Examples
   * ------  -------                      ------------  -------
   * G       era                          text          AD
   * C       century of era (>=0)         number        20
   * Y       year of era (>=0)            year          1996
   *
   * x       weekyear                     year          1996
   * w       week of weekyear             number        27
   * e       day of week                  number        2
   * E       day of week                  text          Tuesday; Tue
   *
   * y       year                         year          1996
   * D       day of year                  number        189
   * M       month of year                month         July; Jul; 07
   * d       day of month                 number        10
   *
   * a       halfday of day               text          PM
   * K       hour of halfday (0~11)       number        0
   * h       clockhour of halfday (1~12)  number        12
   *
   * H       hour of day (0~23)           number        0
   * k       clockhour of day (1~24)      number        24
   * m       minute of hour               number        30
   * s       second of minute             number        55
   * S       fraction of second           number        978
   *
   * z       time zone                    text          Pacific Standard Time; PST
   * Z       time zone offset/id          zone          -0800; -08:00; America/Los_Angeles
   *
   * '       escape for text              delimiter
   * ''      single quote                 literal       '
   * 
*/ public CalendarDateFormatter(String pattern) { dflocal = DateTimeFormat.forPattern(pattern).withZoneUTC(); } public CalendarDateFormatter(String pattern, CalendarTimeZone tz, Calendar cal) { Chronology chron = Calendar.getChronology(cal); dflocal = DateTimeFormat.forPattern(pattern).withChronology(chron).withZone(tz.getJodaTimeZone()); } public CalendarDateFormatter(String pattern, CalendarTimeZone tz) { dflocal = DateTimeFormat.forPattern(pattern).withZone( tz.getJodaTimeZone()); } public String toString(CalendarDate cd) { return dflocal.print(cd.getDateTime()); } public CalendarDate parse(String timeString) { DateTime dt = dflocal.parseDateTime(timeString); Calendar cal = Calendar.get(dt.getChronology().toString()); return new CalendarDate(cal, dt); } public static void main(String arg[]) { CalendarDate cd = CalendarDate.present(); /* {"S", "M", "L", "F", "-"} System.out.printf("%s%n", DateTimeFormat.forStyle("SS").print(cd.getDateTime())); System.out.printf("%s%n", DateTimeFormat.forStyle("MM").print(cd.getDateTime())); System.out.printf("%s%n", DateTimeFormat.forStyle("LL").print(cd.getDateTime())); System.out.printf("%s%n", DateTimeFormat.forStyle("FF").print(cd.getDateTime())); */ System.out.printf("%s%n", cd); System.out.printf("toDateTimeStringISO=%s%n", toDateTimeStringISO(cd)); System.out.printf(" toDateTimeString=%s%n", toDateTimeString(cd)); System.out.printf(" toDateString=%s%n", toDateString(cd)); System.out.printf(" toTimeUnits=%s%n", toTimeUnits(cd)); System.out.printf("===============================%n"); Date d = cd.toDate(); System.out.printf("cd.toDate()=%s%n", toDateTimeString(d)); SimpleDateFormat udunitDF = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.SHORT, Locale.US); udunitDF.setTimeZone(TimeZone.getTimeZone("UTC")); udunitDF.applyPattern("yyyy-MM-dd HH:mm:ss.SSS 'UTC'"); System.out.printf(" udunitDF=%s%n", udunitDF.format(d)); System.out.printf("===============================%n"); DateFormatter df = new DateFormatter(); System.out.printf(" toTimeUnits(date)=%s%n", toTimeUnits(cd)); System.out.printf("toDateTimeString(date)=%s%n", df.toDateTimeString(d)); System.out.printf("toDateOnlyString(date)=%s%n", df.toDateOnlyString(d)); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy