package org.yamcs.utils; import java.util.Calendar; import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * * This class provides times in terms of milliseconds since 1970TAI * @author nm * */ public class TimeEncoding { public static final long INVALID_INSTANT = Long.MIN_VALUE; //these two are used for open intervals public static final long MIN_INSTANT = Long.MIN_VALUE; public static long MAX_INSTANT = 185539080470435999L; static final long GPS_EPOCH_YAMCS_EPOCH_DELTA = 315964819000L; static final long GPS_TAI_DELTA = 19000; static TaiUtcConverter taiUtcConverter; static Pattern iso8601Pattern = Pattern.compile("(\\d+)\\-(\\d{2})\\-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(\\.(\\d{3}))?Z?"); static Pattern doyPattern = Pattern.compile("(\\d+)\\/(\\d+)T(\\d{2}):(\\d{2}):(\\d{2})(\\.(\\d{3}))?"); public static void setUp() throws RuntimeException { try { taiUtcConverter = new TaiUtcConverter(); MAX_INSTANT = 185539080470399999L + taiUtcConverter.diffTaiUtc * 1000; } catch (Exception e) { throw new RuntimeException(e); } } /** * Returns the current wall clock time. Is the same with getWallclockTime * * Should use instead timeService.getMissionTime() * * @return */ @Deprecated public static long currentInstant() { return taiUtcConverter.unixToInstant(System.currentTimeMillis()); } public static long getWallclockTime() { return taiUtcConverter.unixToInstant(System.currentTimeMillis()); } private static void formatOn2Digits(int x, StringBuilder sb) { if(x<10) sb.append("0").append(x); else sb.append(x); } private static void formatOn3Digits(int x, StringBuilder sb) { if(x<10) sb.append("00").append(x); else if(x<100) sb.append("0").append(x); else sb.append(x); } private static void formatOn4Digits(int x, StringBuilder sb) { if(x<10) sb.append("000").append(x); else if(x<100) sb.append("00").append(x); else if(x<1000) sb.append("0").append(x); else sb.append(x); } /** * Returns the instant formatted as utc * yyyy-MM-DDTHH:mm:ss.SSS * @param instant * @return */ public static String toString(long instant) { TaiUtcConverter.DateTimeComponents dtc = taiUtcConverter.instantToUtc(instant); StringBuilder sb=new StringBuilder(); formatOn4Digits(dtc.year, sb);sb.append("-"); formatOn2Digits(dtc.month, sb); sb.append("-"); formatOn2Digits(dtc.day, sb); sb.append("T"); formatOn2Digits(dtc.hour, sb); sb.append(":"); formatOn2Digits(dtc.minute, sb); sb.append(":"); formatOn2Digits(dtc.second, sb); sb.append("."); formatOn3Digits(dtc.millisec, sb); return sb.toString(); } /** * Returns the instant formatted as UTC * yyyy-DDDTHH:mm:ss.SSS * @param instant * @return */ public static String toOrdinalDateTime(long instant) { TaiUtcConverter.DateTimeComponents dtc = taiUtcConverter.instantToUtc(instant); StringBuilder sb=new StringBuilder(); formatOn4Digits(dtc.year, sb);sb.append("-"); formatOn3Digits(dtc.doy, sb); sb.append("T"); formatOn2Digits(dtc.hour, sb); sb.append(":"); formatOn2Digits(dtc.minute, sb); sb.append(":"); formatOn2Digits(dtc.second, sb); sb.append("."); formatOn3Digits(dtc.millisec, sb); return sb.toString(); } /** * Returns the instant in UTC time scale formatted as * YYYY-DDDTHHhMMmSSsSSS * so that is leads to an MS Windows compatible filename * @param instant * @return */ public static String toWinCompatibleDateTime(long instant) { TaiUtcConverter.DateTimeComponents dtc = taiUtcConverter.instantToUtc(instant); StringBuilder sb = new StringBuilder(); formatOn4Digits(dtc.year, sb); sb.append("-"); formatOn3Digits(dtc.doy, sb); sb.append("T"); formatOn2Digits(dtc.hour, sb); sb.append("h"); formatOn2Digits(dtc.minute, sb); sb.append("m"); formatOn2Digits(dtc.second, sb); sb.append("s"); formatOn3Digits(dtc.millisec, sb); return sb.toString(); } public static String toCombinedFormat(long instant) { TaiUtcConverter.DateTimeComponents dtc = taiUtcConverter.instantToUtc(instant); StringBuilder sb=new StringBuilder(); formatOn4Digits(dtc.year, sb);sb.append("-"); formatOn2Digits(dtc.month, sb); sb.append("-"); formatOn2Digits(dtc.day, sb); sb.append("/"); formatOn3Digits(dtc.doy, sb); sb.append("T"); formatOn2Digits(dtc.hour, sb); sb.append(":"); formatOn2Digits(dtc.minute, sb); sb.append(":"); formatOn2Digits(dtc.second, sb); sb.append("."); formatOn3Digits(dtc.millisec, sb); return sb.toString(); } /** * we assume coarseTime to be always positive (corresponding to uint32_t in * C) * @param coarseTime number of seconds from GPS epoch * @param fineTime number of 1/256 seconds * @return */ public static long fromGpsCcsdsTime(int coarseTime, byte fineTime) { long c = ((long) coarseTime) & 0xFFFFFFFFL; return GPS_EPOCH_YAMCS_EPOCH_DELTA + c * 1000 + 1000 * (0xFF & fineTime) / 256; } /** * Conversion from instant to GPS time. * @param instant yamcs time * @return GPS time */ public static GpsCcsdsTime toGpsTime(final long instant) { GpsCcsdsTime gpsTime = new GpsCcsdsTime(); long shiftedMillis = instant - GPS_EPOCH_YAMCS_EPOCH_DELTA; gpsTime.coarseTime = (int) (shiftedMillis / 1000); gpsTime.fineTime = (byte) (((shiftedMillis % 1000) * 256 / 1000)); return gpsTime; } /** * Conversion from current instant to GPS time. * Current time is the *nix time this function is called. * @return GPS time */ public static GpsCcsdsTime getCurrentGpsTime() { return toGpsTime(TimeEncoding.currentInstant()); } /** * Conversion from instant to GPS time (milliseconds since the GPS epoch). * @param instant TimeEncoding instant * * @return GPS time */ public static long toGpsTimeMillisec(final long instant) { return instant - GPS_EPOCH_YAMCS_EPOCH_DELTA; } public static long fromGpsYearSecMillis(int year, int secOfYear, int millis) { Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); cal.clear(); cal.set(Calendar.YEAR, year); cal.add(Calendar.SECOND, secOfYear); cal.add(Calendar.MILLISECOND, millis); return GPS_TAI_DELTA + cal.getTimeInMillis(); } public static TaiUtcConverter.DateTimeComponents toUtc(long instant) { return taiUtcConverter.instantToUtc(instant); } /** * parses an ISO 8601 UTC date into an instant * @param s * @return */ public static long parse(String s) { TaiUtcConverter.DateTimeComponents dtc; Matcher m = iso8601Pattern.matcher(s); if(m.matches()) { int year = Integer.parseInt(m.group(1)); int month = Integer.parseInt(m.group(2)); int day = Integer.parseInt(m.group(3)); int hour = Integer.parseInt(m.group(4)); int minute = Integer.parseInt(m.group(5)); int second = Integer.parseInt(m.group(6)); int millisec =0; if(m.group(7)!=null) { millisec = Integer.parseInt(m.group(8)); } dtc = new TaiUtcConverter.DateTimeComponents(year, month, day, hour, minute, second, millisec); } else { m = doyPattern.matcher(s); if(m.matches()) { int year = Integer.parseInt(m.group(1)); int doy = Integer.parseInt(m.group(2)); int hour = Integer.parseInt(m.group(3)); int minute = Integer.parseInt(m.group(4)); int second = Integer.parseInt(m.group(5)); int millisec = 0; if(m.group(6)!=null) { millisec = Integer.parseInt(m.group(7)); } dtc = new TaiUtcConverter.DateTimeComponents(year, doy, hour, minute, second, millisec); } else { throw new IllegalArgumentException("Cannot parse '"+s+"' with the pattern '"+iso8601Pattern+" or "+doyPattern); } } return taiUtcConverter.utcToInstant(dtc); } /** * Transforms UNIX time (milliseconds since 1970) to instant * @param milliseconds * @return */ public static long fromUnixTime(long milliseconds) { return taiUtcConverter.unixToInstant(milliseconds); } /** * Transforms UNIX time expressed in seconds and microseconds since 1970 to instant * WARNING: this conversion will loose precision (microsecond to millisecond) * * @param seconds * @param microseconds * @return */ public static long fromUnixTime(long seconds, int microseconds) { long millisec = seconds*1000+microseconds/1000; return taiUtcConverter.unixToInstant(millisec); } /** * Transforms instant to UNIX time expressed in milliseconds since 1970 * @param instant * @return */ public static long toUnixTime(long instant) { return taiUtcConverter.instantToUnix(instant); } /** * Transforms the cal from UNIX (millisec since 1970) to instant * @param cal * @return */ public static long fromCalendar(Calendar cal) { return fromUnixTime(cal.getTimeInMillis()); } /** * transforms instant into a java cal containing milliseconds since 1970 * @param instant * @return */ public static Calendar toCalendar(long instant) { if(instant==TimeEncoding.INVALID_INSTANT) return null; long t = taiUtcConverter.instantToUnix(instant); Calendar cal=Calendar.getInstance(); cal.setTimeInMillis(t); return cal; } /** * JavaGps is number of milliseconds since 1970 that assumes no leap seconds * from 1970 to GPS Epoch, and then continues with the leap seconds. * @param instant * @return */ public static long getJavaGpsFromInstant(long instant) { return instant - 19000; } public static long getInstantfromJavaGps(long javagps) { return javagps + 19000; } /** * * @param gpstime number of millisec from GPS epoch * @return */ public static long fromGpsMillisec(long gpstime) { return gpstime + GPS_EPOCH_YAMCS_EPOCH_DELTA; } }