class PackedInstant {

  protected PackedInstant() {}

  public static Instant asInstant(long dateTime) {<FILL_FUNCTION_BODY>}

  protected static long pack(LocalDate date, LocalTime time) {
    if (date == null || time == null) {
      return missingValueIndicator();
    }
    int d = PackedLocalDate.pack(date);
    int t = PackedLocalTime.pack(time);
    return (((long) d) << 32) | (t & 0xffffffffL);
  }

  public static long pack(Instant instant) {
    if (instant == null) {
      return missingValueIndicator();
    }
    LocalDateTime dateTime = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);
    LocalDate date = dateTime.toLocalDate();
    LocalTime time = dateTime.toLocalTime();
    return (pack(date, time));
  }

  public static int date(long packedDateTIme) {
    return (int) (packedDateTIme >> 32);
  }

  public static int time(long packedDateTIme) {
    return (int) packedDateTIme;
  }

  public static String toString(long dateTime) {
    if (dateTime == Long.MIN_VALUE) {
      return "";
    }
    int date = date(dateTime);
    int time = time(dateTime);

    return ""
        + PackedLocalDate.getYear(date)
        + "-"
        + Strings.padStart(Byte.toString(PackedLocalDate.getMonthValue(date)), 2, '0')
        + "-"
        + Strings.padStart(Byte.toString(PackedLocalDate.getDayOfMonth(date)), 2, '0')
        + "T"
        + Strings.padStart(Byte.toString(PackedLocalTime.getHour(time)), 2, '0')
        + ":"
        + Strings.padStart(Byte.toString(PackedLocalTime.getMinute(time)), 2, '0')
        + ":"
        + Strings.padStart(Byte.toString(PackedLocalTime.getSecond(time)), 2, '0')
        + "."
        + Strings.padStart(String.valueOf(PackedLocalTime.getMilliseconds(time)), 3, '0')
        + "Z";
  }

  /**
   * Returns the given packedDateTime with amtToAdd of temporal units added
   *
   * <p>TODO(lwhite): Replace with a native implementation that doesn't convert everything to
   * Instant
   */
  public static long plus(long packedDateTime, long amountToAdd, TemporalUnit unit) {
    Instant dateTime = asInstant(packedDateTime);
    if (dateTime == null) {
      throw new IllegalArgumentException("Cannot do addition on missing value");
    }
    return pack(dateTime.plus(amountToAdd, unit));
  }

  public static boolean isAfter(long packedDateTime, long value) {
    return (packedDateTime != missingValueIndicator()) && packedDateTime > value;
  }

  public static boolean isBefore(long packedDateTime, long value) {
    return (packedDateTime != missingValueIndicator()) && packedDateTime < value;
  }

  public static long create(int date, int time) {
    return (((long) date) << 32) | (time & 0xffffffffL);
  }

  // TODO: packed support for minutesUntil and hoursUnit. These implementations are inefficient
  public static long minutesUntil(long packedDateTimeEnd, long packedDateTimeStart) {
    return ChronoUnit.MINUTES.between(asInstant(packedDateTimeStart), asInstant(packedDateTimeEnd));
  }

  public static long hoursUntil(long packedDateTimeEnd, long packedDateTimeStart) {
    return ChronoUnit.HOURS.between(asInstant(packedDateTimeStart), asInstant(packedDateTimeEnd));
  }

  public static int daysUntil(long packedDateTimeEnd, long packedDateTimeStart) {
    return (int)
        (PackedLocalDate.toEpochDay(date(packedDateTimeEnd))
            - PackedLocalDate.toEpochDay(date(packedDateTimeStart)));
  }

  public static int weeksUntil(long packedDateTimeEnd, long packedDateStart) {
    return daysUntil(packedDateTimeEnd, packedDateStart) / 7;
  }

  public static boolean isEqualTo(long packedDateTime, long value) {
    return DateTimePredicates.isEqualTo.test(packedDateTime, value);
  }

  public static boolean isOnOrAfter(long valueToTest, long valueToTestAgainst) {
    return valueToTest >= valueToTestAgainst;
  }

  public static boolean isOnOrBefore(long valueToTest, long valueToTestAgainst) {
    return isBefore(valueToTest, valueToTestAgainst) || isEqualTo(valueToTest, valueToTestAgainst);
  }
}

    if (dateTime == missingValueIndicator()) {
      return null;
    }
    int date = date(dateTime);
    int time = time(dateTime);
    LocalDate d = PackedLocalDate.asLocalDate(date);
    LocalTime t = PackedLocalTime.asLocalTime(time);
    if (d == null || t == null) {
      return null;
    }
    return LocalDateTime.of(d, t).toInstant(ZoneOffset.UTC);
  
<no_super_class>