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

ucar.nc2.time.CalendarDateUnit 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 com.google.re2j.Matcher;
import com.google.re2j.Pattern;
import java.util.Objects;
import javax.annotation.concurrent.Immutable;
import java.util.Date;
import java.util.Formatter;

/**
 * A Calendar Date Unit: "unit since date"
 *
 * 
 * UNIT since [-]Y[Y[Y[Y]]]-MM-DD[(T| )hh[:mm[:ss[.sss*]]][ [+|-]hh[[:]mm]]]
 *
 * UNIT = "msecs" | "msec" |
          "seconds" | "second" | "secs" | "sec" | "s" |
          "minutes" | "minute" | "mins" | "min" |
          "hours" | "hour" | "hrs" | "hr" | "h" |
          "days" | "day" | "d"
 * 
* * "months since base_date by calendar field" * "years since base_date by calendar field" * * bob simon's proposal: * * "calendar_month since base_date" * "calendar_year since base_date" * * only integer values are allowed (?) * * jon blowers comment: * In your view would the solution to add "by calendar field" to the existing udunits string be acceptable? It’s * backward-compatible with the current interpretation and adds clarification for the cases in which we *do* want to * do calendar-field arithmetic (instead of adding fixed intervals). * There’s an alternative proposition, in which the new units of calendar_month and calendar_year are added, with the * same semantic effect. * (However, personally I like the "by calendar field" solution since it allows other fields to vary between calendars, * e.g. * because of leap-seconds.) * * @author caron * @since 3/18/11 */ @Immutable public class CalendarDateUnit { public static final CalendarDateUnit unixDateUnit = CalendarDateUnit.of(null, CalendarPeriod.Field.Second, CalendarDate.parseISOformat(null, "1970-01-01T00:00:00")); private static final String byCalendarString = "calendar "; // 1 2 3 4 5 public static final String udunitPatternString = "(\\w*)\\s*since\\s*" + CalendarDateFormatter.isodatePatternString; // "([\\+\\-\\d]+)([ Tt]([\\.\\:\\d]*)([ \\+\\-]\\S*)?z?)?$"; // public for testing private static final Pattern udunitPattern = Pattern.compile(udunitPatternString); /** * Create a CalendarDateUnit from a calendar name and a udunit string = "unit since calendarDate" * * @param calendarName must match a calendar enum, or one of its aliases, see ucar.nc2.time.Calendar.get() * @param udunitString "unit since calendarDate" * @return CalendarDateUnit * @throws IllegalArgumentException if udunitString is not paresable */ public static CalendarDateUnit of(String calendarName, String udunitString) { Calendar calt = Calendar.get(calendarName); if (calt == null) calt = Calendar.getDefault(); return new CalendarDateUnit(calt, udunitString); } /** * Create a CalendarDateUnit from a calendar and a udunit string = "unit since calendarDate" * * @param calt use this Calendar, or null for default calendar * @param udunitString "unit since calendarDate" * @return CalendarDateUnit * @throws IllegalArgumentException if udunitString is not paresable */ public static CalendarDateUnit withCalendar(Calendar calt, String udunitString) { if (calt == null) calt = Calendar.getDefault(); return new CalendarDateUnit(calt, udunitString); } /** * Create a CalendarDateUnit from a calendar, a CalendarPeriod.Field, and a base date * * @param calt use this Calendar, or null for default calendar * @param periodField a CalendarPeriod.Field like Hour or second * @param baseDate "since baseDate" * @return CalendarDateUnit */ public static CalendarDateUnit of(Calendar calt, CalendarPeriod.Field periodField, CalendarDate baseDate) { if (calt == null) calt = Calendar.getDefault(); return new CalendarDateUnit(calt, periodField, baseDate); } //////////////////////////////////////////////////////////////////////////////////////// private final Calendar cal; // private final String unitString; private final CalendarPeriod period; private final CalendarPeriod.Field periodField; private final CalendarDate baseDate; private final boolean isCalendarField; private CalendarDateUnit(Calendar calt, String dateUnitString) { dateUnitString = dateUnitString.trim(); dateUnitString = dateUnitString.replaceAll("\\s+", " "); dateUnitString = dateUnitString.toLowerCase(); isCalendarField = dateUnitString.startsWith(byCalendarString); if (isCalendarField) { dateUnitString = dateUnitString.substring(byCalendarString.length()).trim(); } Matcher m = udunitPattern.matcher(dateUnitString); if (!m.matches()) { throw new IllegalArgumentException(dateUnitString + " does not match " + udunitPatternString); } String unitString = m.group(1); period = CalendarPeriod.of(unitString); periodField = CalendarPeriod.fromUnitString(unitString); int pos = dateUnitString.indexOf("since"); String iso = dateUnitString.substring(pos + 5); baseDate = CalendarDateFormatter.isoStringToCalendarDate(calt, iso); // calendar might change !! calt = baseDate.getCalendar(); this.cal = calt; } private CalendarDateUnit(Calendar calt, CalendarPeriod.Field periodField, CalendarDate baseDate) { this.cal = calt; this.periodField = periodField; this.period = CalendarPeriod.of(1, periodField); this.baseDate = baseDate; isCalendarField = periodField == CalendarPeriod.Field.Month || periodField == CalendarPeriod.Field.Year; } // given a CalendarDate, find the values in this unit (secs, days, etc) from the baseDate // inverse of makeCalendarDate public double makeOffsetFromRefDate(CalendarDate date) { if (isCalendarField) { if (date.equals(baseDate)) return 0.0; return date.getDifference(baseDate, periodField); } else { long msecs = date.getDifferenceInMsecs(baseDate); return msecs / period.getValueInMillisecs(); } } // given a value in this unit (secs, days, etc), create the CalendarDate from the RefDate // inverse of makeOffsetFromRefDate public CalendarDate makeCalendarDate(double value) { if (isCalendarField) return baseDate.add(CalendarPeriod.of((int) value, periodField)); // LOOK int vs double else return baseDate.add(value, periodField); } public CalendarDate makeCalendarDate(int value) { if (isCalendarField) return baseDate.add(CalendarPeriod.of(value, periodField)); else return baseDate.add(value, periodField); } public String getUdUnit() { return toString(); } public CalendarDate getBaseCalendarDate() { return baseDate; } public CalendarPeriod getCalendarPeriod() { return period; } public CalendarPeriod.Field getCalendarField() { return periodField; } public Calendar getCalendar() { return cal; } public boolean isCalendarField() { return isCalendarField; } ///////////////////////////////////// @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; CalendarDateUnit that = (CalendarDateUnit) o; return isCalendarField == that.isCalendarField && cal == that.cal && period.equals(that.period) && periodField == that.periodField && baseDate.equals(that.baseDate); } @Override public int hashCode() { return Objects.hash(cal, period, periodField, baseDate, isCalendarField); } @Override public String toString() { Formatter f = new Formatter(); if (isCalendarField) f.format("%s", byCalendarString); f.format("%s since %s", periodField, CalendarDateFormatter.toDateTimeStringISO(baseDate)); return f.toString(); } ///////////////////////////////////////////// // testing Date getBaseDate() { return baseDate.toDate(); } } /* * * The acceptable units for time are listed in the udunits.dat file. The most commonly used of these strings (and their * abbreviations) * includes day (d), hour (hr, h), minute (min) and second (sec, s). Plural forms are also acceptable. The reference * time string * (appearing after the identifier since) may include date alone; date and time; or date, time, and time zone. * The reference time is required. A reference time in year 0 has a special meaning (see Section 7.4, Climatological * Statistics). * * Note: if the time zone is omitted the default is UTC, and if both time and time zone are omitted the default is * 00:00:00 UTC. * * We recommend that the unit year be used with caution. The Udunits package defines a year to be exactly 365.242198781 * days * (the interval between 2 successive passages of the sun through vernal equinox). It is not a calendar year. * Udunits includes the following definitions for years: a common_year is 365 days, a leap_year is 366 days, a * Julian_year is 365.25 days, * and a Gregorian_year is 365.2425 days. * * For similar reasons the unit month, which is defined in udunits.dat to be exactly year/12, should also be used with * caution. * * Example 4.4. Time axis * * double time(time) ; * time:long_name = "time" ; * time:units = "days since 1990-1-1 0:0:0" ; * * * A time coordinate is identifiable from its units string alone. The Udunits routines utScan() and utIsTime() can be * used to * make this determination. * * Optionally, the time coordinate may be indicated additionally by providing the standard_name attribute with an * appropriate * value, and/or the axis attribute with the value */




© 2015 - 2025 Weber Informatics LLC | Privacy Policy