diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/localization/DateWrapper.java b/extractor/src/main/java/org/schabi/newpipe/extractor/localization/DateWrapper.java
index 2f0a2bdd2a..00dcd124df 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/localization/DateWrapper.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/localization/DateWrapper.java
@@ -1,12 +1,16 @@
package org.schabi.newpipe.extractor.localization;
+import org.schabi.newpipe.extractor.exceptions.ParsingException;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import java.io.Serializable;
+import java.time.Instant;
+import java.time.LocalDateTime;
import java.time.OffsetDateTime;
+import java.time.ZoneId;
import java.time.ZoneOffset;
-import java.util.Calendar;
-import java.util.GregorianCalendar;
+import java.time.format.DateTimeParseException;
/**
* A wrapper class that provides a field to describe if the date/time is precise or just an
@@ -14,52 +18,61 @@
*/
public class DateWrapper implements Serializable {
@Nonnull
- private final OffsetDateTime offsetDateTime;
+ private final Instant instant;
private final boolean isApproximation;
- /**
- * @deprecated Use {@link #DateWrapper(OffsetDateTime)} instead.
- */
- @Deprecated
- public DateWrapper(@Nonnull final Calendar calendar) {
- //noinspection deprecation
- this(calendar, false);
- }
-
- /**
- * @deprecated Use {@link #DateWrapper(OffsetDateTime, boolean)} instead.
- */
- @Deprecated
- public DateWrapper(@Nonnull final Calendar calendar, final boolean isApproximation) {
- this(OffsetDateTime.ofInstant(calendar.toInstant(), ZoneOffset.UTC), isApproximation);
- }
-
public DateWrapper(@Nonnull final OffsetDateTime offsetDateTime) {
this(offsetDateTime, false);
}
public DateWrapper(@Nonnull final OffsetDateTime offsetDateTime,
final boolean isApproximation) {
- this.offsetDateTime = offsetDateTime.withOffsetSameInstant(ZoneOffset.UTC);
+ this(offsetDateTime.toInstant(), isApproximation);
+ }
+
+ public DateWrapper(@Nonnull final Instant instant) {
+ this(instant, false);
+ }
+
+ public DateWrapper(@Nonnull final Instant instant, final boolean isApproximation) {
+ this.instant = instant;
this.isApproximation = isApproximation;
}
+ public DateWrapper(@Nonnull final LocalDateTime dateTime, final boolean isApproximation) {
+ this(dateTime.atZone(ZoneId.systemDefault()).toInstant(), isApproximation);
+ }
+
/**
- * @return the wrapped date/time as a {@link Calendar}.
- * @deprecated use {@link #offsetDateTime()} instead.
+ * @return the wrapped {@link Instant}
*/
- @Deprecated
@Nonnull
- public Calendar date() {
- return GregorianCalendar.from(offsetDateTime.toZonedDateTime());
+ public Instant getInstant() {
+ return instant;
}
/**
- * @return the wrapped date/time.
+ * @return the wrapped {@link Instant} as an {@link OffsetDateTime} set to UTC.
*/
@Nonnull
public OffsetDateTime offsetDateTime() {
- return offsetDateTime;
+ return instant.atOffset(ZoneOffset.UTC);
+ }
+
+ /**
+ * @return the wrapped {@link Instant} as a {@link LocalDateTime} in the current time zone.
+ */
+ @Nonnull
+ public LocalDateTime getLocalDateTime() {
+ return getLocalDateTime(ZoneId.systemDefault());
+ }
+
+ /**
+ * @return the wrapped {@link Instant} as a {@link LocalDateTime} in the given time zone.
+ */
+ @Nonnull
+ public LocalDateTime getLocalDateTime(@Nonnull final ZoneId zoneId) {
+ return LocalDateTime.ofInstant(instant, zoneId);
}
/**
@@ -73,8 +86,42 @@ public boolean isApproximation() {
@Override
public String toString() {
return "DateWrapper{"
- + "offsetDateTime=" + offsetDateTime
+ + "instant=" + instant
+ ", isApproximation=" + isApproximation
+ '}';
}
+
+ /**
+ * Parses a date string that matches the ISO-8601 {@link OffsetDateTime} pattern, e.g.
+ * "2011-12-03T10:15:30+01:00".
+ *
+ * @param date The date string
+ * @return a non-approximate {@link DateWrapper}, or null if the string is null
+ * @throws ParsingException if the string does not match the expected format
+ */
+ @Nullable
+ public static DateWrapper fromOffsetDateTime(final String date) throws ParsingException {
+ try {
+ return date != null ? new DateWrapper(OffsetDateTime.parse(date)) : null;
+ } catch (final DateTimeParseException e) {
+ throw new ParsingException("Could not parse date: \"" + date + "\"", e);
+ }
+ }
+
+ /**
+ * Parses a date string that matches the ISO-8601 {@link Instant} pattern, e.g.
+ * "2011-12-03T10:15:30Z".
+ *
+ * @param date The date string
+ * @return a non-approximate {@link DateWrapper}, or null if the string is null
+ * @throws ParsingException if the string does not match the expected format
+ */
+ @Nullable
+ public static DateWrapper fromInstant(final String date) throws ParsingException {
+ try {
+ return date != null ? new DateWrapper(Instant.parse(date)) : null;
+ } catch (final DateTimeParseException e) {
+ throw new ParsingException("Could not parse date: \"" + date + "\"", e);
+ }
+ }
}
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/localization/TimeAgoParser.java b/extractor/src/main/java/org/schabi/newpipe/extractor/localization/TimeAgoParser.java
index 619de9e74b..88e04b803c 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/localization/TimeAgoParser.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/localization/TimeAgoParser.java
@@ -4,8 +4,7 @@
import org.schabi.newpipe.extractor.timeago.PatternsHolder;
import org.schabi.newpipe.extractor.utils.Parser;
-import java.time.OffsetDateTime;
-import java.time.ZoneOffset;
+import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Map;
import java.util.regex.Pattern;
@@ -16,20 +15,7 @@
*/
public class TimeAgoParser {
private final PatternsHolder patternsHolder;
- private final OffsetDateTime now;
-
- /**
- * Creates a helper to parse upload dates in the format '2 days ago'.
- *
- * Instantiate a new {@link TimeAgoParser} every time you extract a new batch of items.
- *
- *
- * @param patternsHolder An object that holds the "time ago" patterns, special cases, and the
- * language word separator.
- */
- public TimeAgoParser(final PatternsHolder patternsHolder) {
- this(patternsHolder, OffsetDateTime.now(ZoneOffset.UTC));
- }
+ private final LocalDateTime now;
/**
* Creates a helper to parse upload dates in the format '2 days ago'.
@@ -41,7 +27,7 @@ public TimeAgoParser(final PatternsHolder patternsHolder) {
* language word separator.
* @param now The current time
*/
- public TimeAgoParser(final PatternsHolder patternsHolder, final OffsetDateTime now) {
+ public TimeAgoParser(final PatternsHolder patternsHolder, final LocalDateTime now) {
this.patternsHolder = patternsHolder;
this.now = now;
}
@@ -118,34 +104,13 @@ private boolean textualDateMatches(final String textualDate, final String agoPhr
}
private DateWrapper getResultFor(final int timeAgoAmount, final ChronoUnit chronoUnit) {
- OffsetDateTime offsetDateTime = now;
- boolean isApproximation = false;
-
- switch (chronoUnit) {
- case SECONDS:
- case MINUTES:
- case HOURS:
- offsetDateTime = offsetDateTime.minus(timeAgoAmount, chronoUnit);
- break;
-
- case DAYS:
- case WEEKS:
- case MONTHS:
- offsetDateTime = offsetDateTime.minus(timeAgoAmount, chronoUnit);
- isApproximation = true;
- break;
-
- case YEARS:
+ final var localDateTime = chronoUnit == ChronoUnit.YEARS
// minusDays is needed to prevent `PrettyTime` from showing '12 months ago'.
- offsetDateTime = offsetDateTime.minusYears(timeAgoAmount).minusDays(1);
- isApproximation = true;
- break;
- }
-
- if (isApproximation) {
- offsetDateTime = offsetDateTime.truncatedTo(ChronoUnit.HOURS);
- }
-
- return new DateWrapper(offsetDateTime, isApproximation);
+ ? now.minusYears(timeAgoAmount).minusDays(1)
+ : now.minus(timeAgoAmount, chronoUnit);
+ final boolean isApproximate = chronoUnit.isDateBased();
+ final var resolvedDateTime =
+ isApproximate ? localDateTime.truncatedTo(ChronoUnit.DAYS) : localDateTime;
+ return new DateWrapper(resolvedDateTime, isApproximate);
}
}
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/localization/TimeAgoPatternsManager.java b/extractor/src/main/java/org/schabi/newpipe/extractor/localization/TimeAgoPatternsManager.java
index 47889a5d32..67cfb93a64 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/localization/TimeAgoPatternsManager.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/localization/TimeAgoPatternsManager.java
@@ -3,7 +3,7 @@
import org.schabi.newpipe.extractor.timeago.PatternsHolder;
import org.schabi.newpipe.extractor.timeago.PatternsManager;
-import java.time.OffsetDateTime;
+import java.time.LocalDateTime;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -20,25 +20,13 @@ private static PatternsHolder getPatternsFor(@Nonnull final Localization localiz
@Nullable
public static TimeAgoParser getTimeAgoParserFor(@Nonnull final Localization localization) {
- final PatternsHolder holder = getPatternsFor(localization);
-
- if (holder == null) {
- return null;
- }
-
- return new TimeAgoParser(holder);
+ return getTimeAgoParserFor(localization, LocalDateTime.now());
}
@Nullable
- public static TimeAgoParser getTimeAgoParserFor(
- @Nonnull final Localization localization,
- @Nonnull final OffsetDateTime now) {
+ public static TimeAgoParser getTimeAgoParserFor(@Nonnull final Localization localization,
+ @Nonnull final LocalDateTime now) {
final PatternsHolder holder = getPatternsFor(localization);
-
- if (holder == null) {
- return null;
- }
-
- return new TimeAgoParser(holder, now);
+ return holder == null ? null : new TimeAgoParser(holder, now);
}
}
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/bandcamp/extractors/BandcampExtractorHelper.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/bandcamp/extractors/BandcampExtractorHelper.java
index b230079109..99f026d7ed 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/bandcamp/extractors/BandcampExtractorHelper.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/bandcamp/extractors/BandcampExtractorHelper.java
@@ -204,7 +204,7 @@ public static DateWrapper parseDate(final String textDate) throws ParsingExcepti
try {
final ZonedDateTime zonedDateTime = ZonedDateTime.parse(textDate,
DateTimeFormatter.ofPattern("dd MMM yyyy HH:mm:ss zzz", Locale.ENGLISH));
- return new DateWrapper(zonedDateTime.toOffsetDateTime(), false);
+ return new DateWrapper(zonedDateTime.toInstant());
} catch (final DateTimeException e) {
throw new ParsingException("Could not parse date '" + textDate + "'", e);
}
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCParsingHelper.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCParsingHelper.java
index 2a4ea6a9f8..89ef3943b0 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCParsingHelper.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCParsingHelper.java
@@ -8,15 +8,12 @@
import org.schabi.newpipe.extractor.Image.ResolutionLevel;
import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
-import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
import org.schabi.newpipe.extractor.localization.Localization;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
-import java.time.OffsetDateTime;
-import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -33,15 +30,6 @@ public final class MediaCCCParsingHelper {
private MediaCCCParsingHelper() { }
- public static OffsetDateTime parseDateFrom(final String textualUploadDate)
- throws ParsingException {
- try {
- return OffsetDateTime.parse(textualUploadDate);
- } catch (final DateTimeParseException e) {
- throw new ParsingException("Could not parse date: \"" + textualUploadDate + "\"", e);
- }
- }
-
/**
* Check whether an id is a live stream id
* @param id the {@code id} to check
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKiosk.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKiosk.java
index d38d65fd53..6223304597 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKiosk.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKiosk.java
@@ -52,12 +52,10 @@ public InfoItemsPage getInitialPage() throws IOException, Extrac
// Streams in the recent kiosk are not ordered by the release date.
// Sort them to have the latest stream at the beginning of the list.
- final Comparator comparator = Comparator
- .comparing(StreamInfoItem::getUploadDate, Comparator
- .nullsLast(Comparator.comparing(DateWrapper::offsetDateTime)))
+ final var comparator = Comparator.comparing(StreamInfoItem::getUploadDate,
+ Comparator.nullsLast(Comparator.comparing(DateWrapper::getInstant)))
.reversed();
- final StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId(),
- comparator);
+ final var collector = new StreamInfoItemsCollector(getServiceId(), comparator);
events.stream()
.filter(JsonObject.class::isInstance)
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKioskExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKioskExtractor.java
index df25b28d89..a8d5ae3c36 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKioskExtractor.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKioskExtractor.java
@@ -92,6 +92,6 @@ public String getTextualUploadDate() throws ParsingException {
public DateWrapper getUploadDate() throws ParsingException {
final ZonedDateTime zonedDateTime = ZonedDateTime.parse(event.getString("date"),
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSzzzz"));
- return new DateWrapper(zonedDateTime.toOffsetDateTime(), false);
+ return new DateWrapper(zonedDateTime.toInstant());
}
}
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCStreamExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCStreamExtractor.java
index 99ddf5e08c..0cb0ac4de8 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCStreamExtractor.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCStreamExtractor.java
@@ -2,7 +2,6 @@
import static org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCParsingHelper.getImageListFromLogoImageUrl;
import static org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCParsingHelper.getThumbnailsFromStreamItem;
-import static org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCParsingHelper.parseDateFrom;
import static org.schabi.newpipe.extractor.stream.AudioStream.UNKNOWN_BITRATE;
import static org.schabi.newpipe.extractor.stream.Stream.ID_UNKNOWN;
@@ -37,6 +36,7 @@
import java.util.Locale;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
public class MediaCCCStreamExtractor extends StreamExtractor {
private JsonObject data;
@@ -46,16 +46,16 @@ public MediaCCCStreamExtractor(final StreamingService service, final LinkHandler
super(service, linkHandler);
}
- @Nonnull
+ @Nullable
@Override
public String getTextualUploadDate() {
return data.getString("release_date");
}
- @Nonnull
+ @Nullable
@Override
public DateWrapper getUploadDate() throws ParsingException {
- return new DateWrapper(parseDateFrom(getTextualUploadDate()));
+ return DateWrapper.fromOffsetDateTime(getTextualUploadDate());
}
@Nonnull
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/infoItems/MediaCCCStreamInfoItemExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/infoItems/MediaCCCStreamInfoItemExtractor.java
index ec9d00f3a0..e165d6c438 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/infoItems/MediaCCCStreamInfoItemExtractor.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/infoItems/MediaCCCStreamInfoItemExtractor.java
@@ -4,7 +4,6 @@
import org.schabi.newpipe.extractor.Image;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.localization.DateWrapper;
-import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCParsingHelper;
import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor;
import org.schabi.newpipe.extractor.stream.StreamType;
@@ -65,11 +64,8 @@ public String getTextualUploadDate() {
@Nullable
@Override
public DateWrapper getUploadDate() throws ParsingException {
- final String date = getTextualUploadDate();
- if (date == null) {
- return null; // event is in the future...
- }
- return new DateWrapper(MediaCCCParsingHelper.parseDateFrom(date));
+ // if null, event is in the future...
+ return DateWrapper.fromOffsetDateTime(getTextualUploadDate());
}
@Override
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/peertube/PeertubeParsingHelper.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/peertube/PeertubeParsingHelper.java
index 4e8bd2d350..3d87ad88cb 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/peertube/PeertubeParsingHelper.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/peertube/PeertubeParsingHelper.java
@@ -18,10 +18,6 @@
import org.schabi.newpipe.extractor.utils.Parser;
import javax.annotation.Nonnull;
-import java.time.Instant;
-import java.time.OffsetDateTime;
-import java.time.ZoneOffset;
-import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -48,15 +44,6 @@ public static void validate(final JsonObject json) throws ContentNotAvailableExc
}
}
- public static OffsetDateTime parseDateFrom(final String textualUploadDate)
- throws ParsingException {
- try {
- return OffsetDateTime.ofInstant(Instant.parse(textualUploadDate), ZoneOffset.UTC);
- } catch (final DateTimeParseException e) {
- throw new ParsingException("Could not parse date: \"" + textualUploadDate + "\"", e);
- }
- }
-
public static Page getNextPage(final String prevPageUrl, final long total) {
final String prevStart;
try {
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/peertube/extractors/PeertubeCommentsInfoItemExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/peertube/extractors/PeertubeCommentsInfoItemExtractor.java
index 57a0444906..80c8bbbc70 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/peertube/extractors/PeertubeCommentsInfoItemExtractor.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/peertube/extractors/PeertubeCommentsInfoItemExtractor.java
@@ -23,7 +23,6 @@
import static org.schabi.newpipe.extractor.services.peertube.extractors.PeertubeCommentsExtractor.CHILDREN;
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.getAvatarsFromOwnerAccountOrVideoChannelObject;
-import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.parseDateFrom;
public class PeertubeCommentsInfoItemExtractor implements CommentsInfoItemExtractor {
@Nonnull
@@ -73,8 +72,7 @@ public String getTextualUploadDate() throws ParsingException {
@Override
public DateWrapper getUploadDate() throws ParsingException {
- final String textualUploadDate = getTextualUploadDate();
- return new DateWrapper(parseDateFrom(textualUploadDate));
+ return DateWrapper.fromInstant(getTextualUploadDate());
}
@Nonnull
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/peertube/extractors/PeertubeStreamExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/peertube/extractors/PeertubeStreamExtractor.java
index 5406368854..bf0e02b593 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/peertube/extractors/PeertubeStreamExtractor.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/peertube/extractors/PeertubeStreamExtractor.java
@@ -79,13 +79,7 @@ public String getTextualUploadDate() throws ParsingException {
@Override
public DateWrapper getUploadDate() throws ParsingException {
- final String textualUploadDate = getTextualUploadDate();
-
- if (textualUploadDate == null) {
- return null;
- }
-
- return new DateWrapper(PeertubeParsingHelper.parseDateFrom(textualUploadDate));
+ return DateWrapper.fromInstant(getTextualUploadDate());
}
@Nonnull
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/peertube/extractors/PeertubeStreamInfoItemExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/peertube/extractors/PeertubeStreamInfoItemExtractor.java
index 46aae43ccf..7016fe047f 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/peertube/extractors/PeertubeStreamInfoItemExtractor.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/peertube/extractors/PeertubeStreamInfoItemExtractor.java
@@ -14,7 +14,6 @@
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.getAvatarsFromOwnerAccountOrVideoChannelObject;
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.getThumbnailsFromPlaylistOrVideoItem;
-import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.parseDateFrom;
public class PeertubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
@@ -85,13 +84,7 @@ public String getTextualUploadDate() throws ParsingException {
@Override
public DateWrapper getUploadDate() throws ParsingException {
- final String textualUploadDate = getTextualUploadDate();
-
- if (textualUploadDate == null) {
- return null;
- }
-
- return new DateWrapper(parseDateFrom(textualUploadDate));
+ return DateWrapper.fromInstant(getTextualUploadDate());
}
@Override
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudParsingHelper.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudParsingHelper.java
index ae8fd77d6d..aeff6bd363 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudParsingHelper.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudParsingHelper.java
@@ -23,6 +23,7 @@
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
+import org.schabi.newpipe.extractor.localization.DateWrapper;
import org.schabi.newpipe.extractor.services.soundcloud.extractors.SoundcloudChannelInfoItemExtractor;
import org.schabi.newpipe.extractor.services.soundcloud.extractors.SoundcloudPlaylistInfoItemExtractor;
import org.schabi.newpipe.extractor.services.soundcloud.extractors.SoundcloudLikesInfoItemExtractor;
@@ -133,17 +134,17 @@ public static synchronized String clientId() throws ExtractionException, IOExcep
throw new ExtractionException("Couldn't extract client id");
}
- public static OffsetDateTime parseDateFrom(final String textualUploadDate)
- throws ParsingException {
+ @Nullable
+ public static DateWrapper parseDate(final String uploadDate) throws ParsingException {
try {
- return OffsetDateTime.parse(textualUploadDate);
- } catch (final DateTimeParseException e1) {
+ return DateWrapper.fromInstant(uploadDate);
+ } catch (final DateTimeParseException e) {
try {
- return OffsetDateTime.parse(textualUploadDate, DateTimeFormatter
- .ofPattern("yyyy/MM/dd HH:mm:ss +0000"));
- } catch (final DateTimeParseException e2) {
- throw new ParsingException("Could not parse date: \"" + textualUploadDate + "\""
- + ", " + e1.getMessage(), e2);
+ return new DateWrapper(OffsetDateTime.parse(uploadDate,
+ DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss +0000")));
+ } catch (final DateTimeParseException e1) {
+ e1.addSuppressed(e);
+ throw new ParsingException("Could not parse date: \"" + uploadDate + "\"", e1);
}
}
}
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/extractors/SoundcloudCommentsInfoItemExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/extractors/SoundcloudCommentsInfoItemExtractor.java
index 5ff6d29db6..d5446e56bb 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/extractors/SoundcloudCommentsInfoItemExtractor.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/extractors/SoundcloudCommentsInfoItemExtractor.java
@@ -13,7 +13,7 @@
import java.util.Objects;
import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper.getAllImagesFromArtworkOrAvatarUrl;
-import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper.parseDateFrom;
+import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper.parseDate;
public class SoundcloudCommentsInfoItemExtractor implements CommentsInfoItemExtractor {
private final JsonObject json;
@@ -69,7 +69,7 @@ public String getTextualUploadDate() {
@Nullable
@Override
public DateWrapper getUploadDate() throws ParsingException {
- return new DateWrapper(parseDateFrom(getTextualUploadDate()));
+ return parseDate(getTextualUploadDate());
}
@Override
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/extractors/SoundcloudStreamExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/extractors/SoundcloudStreamExtractor.java
index 595862bde7..4de7114de6 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/extractors/SoundcloudStreamExtractor.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/extractors/SoundcloudStreamExtractor.java
@@ -5,7 +5,7 @@
import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper.getAllImagesFromArtworkOrAvatarUrl;
import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper.getAllImagesFromTrackObject;
import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper.getAvatarUrl;
-import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper.parseDateFrom;
+import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper.parseDate;
import static org.schabi.newpipe.extractor.stream.Stream.ID_UNKNOWN;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
@@ -88,18 +88,16 @@ public String getName() {
return track.getString("title");
}
- @Nonnull
+ @Nullable
@Override
public String getTextualUploadDate() {
- return track.getString("created_at")
- .replace("T", " ")
- .replace("Z", "");
+ return track.getString("created_at");
}
- @Nonnull
+ @Nullable
@Override
public DateWrapper getUploadDate() throws ParsingException {
- return new DateWrapper(parseDateFrom(track.getString("created_at")));
+ return parseDate(getTextualUploadDate());
}
@Nonnull
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/extractors/SoundcloudStreamInfoItemExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/extractors/SoundcloudStreamInfoItemExtractor.java
index 6fd6232e97..f12301738c 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/extractors/SoundcloudStreamInfoItemExtractor.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/extractors/SoundcloudStreamInfoItemExtractor.java
@@ -13,7 +13,7 @@
import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper.getAllImagesFromArtworkOrAvatarUrl;
import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper.getAllImagesFromTrackObject;
-import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper.parseDateFrom;
+import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper.parseDate;
import static org.schabi.newpipe.extractor.utils.Utils.replaceHttpWithHttps;
public class SoundcloudStreamInfoItemExtractor implements StreamInfoItemExtractor {
@@ -68,7 +68,7 @@ public String getTextualUploadDate() {
@Override
public DateWrapper getUploadDate() throws ParsingException {
- return new DateWrapper(parseDateFrom(getTextualUploadDate()));
+ return parseDate(getTextualUploadDate());
}
@Override
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeParsingHelper.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeParsingHelper.java
index 70aac8d2b5..df295f0d5c 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeParsingHelper.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeParsingHelper.java
@@ -67,10 +67,6 @@
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
-import java.time.LocalDate;
-import java.time.OffsetDateTime;
-import java.time.ZoneOffset;
-import java.time.format.DateTimeParseException;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
@@ -294,20 +290,6 @@ public static String getFeedUrlFrom(@Nonnull final String channelIdOrUser) {
}
}
- public static OffsetDateTime parseDateFrom(final String textualUploadDate)
- throws ParsingException {
- try {
- return OffsetDateTime.parse(textualUploadDate);
- } catch (final DateTimeParseException e) {
- try {
- return LocalDate.parse(textualUploadDate).atStartOfDay().atOffset(ZoneOffset.UTC);
- } catch (final DateTimeParseException e1) {
- throw new ParsingException("Could not parse date: \"" + textualUploadDate + "\"",
- e1);
- }
- }
- }
-
/**
* Checks if the given playlist id is a YouTube Mix (auto-generated playlist)
* Ids from a YouTube Mix start with "RD"
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeFeedInfoItemExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeFeedInfoItemExtractor.java
index d917eb2d7b..748c96ecad 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeFeedInfoItemExtractor.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeFeedInfoItemExtractor.java
@@ -10,8 +10,6 @@
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
-import java.time.OffsetDateTime;
-import java.time.format.DateTimeParseException;
import java.util.List;
public class YoutubeFeedInfoItemExtractor implements StreamInfoItemExtractor {
@@ -69,12 +67,7 @@ public String getTextualUploadDate() {
@Nullable
@Override
public DateWrapper getUploadDate() throws ParsingException {
- try {
- return new DateWrapper(OffsetDateTime.parse(getTextualUploadDate()));
- } catch (final DateTimeParseException e) {
- throw new ParsingException("Could not parse date (\"" + getTextualUploadDate() + "\")",
- e);
- }
+ return DateWrapper.fromOffsetDateTime(getTextualUploadDate());
}
@Override
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java
index eb050fd13e..e6fa782ce7 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java
@@ -87,20 +87,23 @@
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
-import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
+import java.util.Optional;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class YoutubeStreamExtractor extends StreamExtractor {
+ private static final String PREMIERED = "Premiered ";
+ private static final String PREMIERED_ON = "Premiered on ";
@Nullable
private static PoTokenProvider poTokenProvider;
@@ -168,79 +171,74 @@ public String getName() throws ParsingException {
@Nullable
@Override
- public String getTextualUploadDate() throws ParsingException {
- if (!playerMicroFormatRenderer.getString("uploadDate", "").isEmpty()) {
- return playerMicroFormatRenderer.getString("uploadDate");
- } else if (!playerMicroFormatRenderer.getString("publishDate", "").isEmpty()) {
- return playerMicroFormatRenderer.getString("publishDate");
+ public String getTextualUploadDate() {
+ String timestamp = playerMicroFormatRenderer.getString("uploadDate", "");
+ if (timestamp.isEmpty()) {
+ timestamp = playerMicroFormatRenderer.getString("publishDate", "");
+ }
+ if (!timestamp.isEmpty()) {
+ return timestamp;
}
- final JsonObject liveDetails = playerMicroFormatRenderer.getObject(
- "liveBroadcastDetails");
- if (!liveDetails.getString("endTimestamp", "").isEmpty()) {
- // an ended live stream
- return liveDetails.getString("endTimestamp");
- } else if (!liveDetails.getString("startTimestamp", "").isEmpty()) {
+ final var liveDetails = playerMicroFormatRenderer.getObject("liveBroadcastDetails");
+ timestamp = liveDetails.getString("endTimestamp", ""); // an ended live stream
+ if (timestamp.isEmpty()) {
// a running live stream
- return liveDetails.getString("startTimestamp");
+ timestamp = liveDetails.getString("startTimestamp", "");
+ }
+ if (!timestamp.isEmpty()) {
+ return timestamp;
} else if (getStreamType() == StreamType.LIVE_STREAM) {
// this should never be reached, but a live stream without upload date is valid
return null;
}
- final String videoPrimaryInfoRendererDateText =
- getTextFromObject(getVideoPrimaryInfoRenderer().getObject("dateText"));
-
- if (videoPrimaryInfoRendererDateText != null) {
- if (videoPrimaryInfoRendererDateText.startsWith("Premiered")) {
- final String time = videoPrimaryInfoRendererDateText.substring(13);
-
- try { // Premiered 20 hours ago
- final TimeAgoParser timeAgoParser = TimeAgoPatternsManager.getTimeAgoParserFor(
- new Localization("en"));
- final OffsetDateTime parsedTime = timeAgoParser.parse(time).offsetDateTime();
- return DateTimeFormatter.ISO_LOCAL_DATE.format(parsedTime);
- } catch (final Exception ignored) {
- }
-
- try { // Premiered Feb 21, 2020
- final LocalDate localDate = LocalDate.parse(time,
- DateTimeFormatter.ofPattern("MMM dd, yyyy", Locale.ENGLISH));
- return DateTimeFormatter.ISO_LOCAL_DATE.format(localDate);
- } catch (final Exception ignored) {
- }
-
- try { // Premiered on 21 Feb 2020
- final LocalDate localDate = LocalDate.parse(time,
- DateTimeFormatter.ofPattern("dd MMM yyyy", Locale.ENGLISH));
- return DateTimeFormatter.ISO_LOCAL_DATE.format(localDate);
- } catch (final Exception ignored) {
- }
- }
-
- try {
- // TODO: this parses English formatted dates only, we need a better approach to
- // parse the textual date
- final LocalDate localDate = LocalDate.parse(videoPrimaryInfoRendererDateText,
- DateTimeFormatter.ofPattern("dd MMM yyyy", Locale.ENGLISH));
- return DateTimeFormatter.ISO_LOCAL_DATE.format(localDate);
- } catch (final Exception e) {
- throw new ParsingException("Could not get upload date", e);
- }
+ final var textObject = getVideoPrimaryInfoRenderer().getObject("dateText");
+ final String rendererDateText = getTextFromObject(textObject);
+ if (rendererDateText == null) {
+ return null;
+ } else if (rendererDateText.startsWith(PREMIERED_ON)) { // Premiered on 21 Feb 2020
+ return rendererDateText.substring(PREMIERED_ON.length());
+ } else if (rendererDateText.startsWith(PREMIERED)) {
+ // Premiered 20 hours ago / Premiered Feb 21, 2020
+ return rendererDateText.substring(PREMIERED.length());
+ } else {
+ return rendererDateText;
}
-
- throw new ParsingException("Could not get upload date");
}
@Override
public DateWrapper getUploadDate() throws ParsingException {
- final String textualUploadDate = getTextualUploadDate();
+ final String dateText = getTextualUploadDate();
+ try {
+ return DateWrapper.fromOffsetDateTime(dateText);
+ } catch (final ParsingException e) {
+ // Try other patterns first
+ }
- if (isNullOrEmpty(textualUploadDate)) {
- return null;
+ try { // Premiered 20 hours ago
+ final var localization = new Localization("en");
+ return TimeAgoPatternsManager.getTimeAgoParserFor(localization).parse(dateText);
+ } catch (final ParsingException e) {
+ // Try other patterns first
}
- return new DateWrapper(YoutubeParsingHelper.parseDateFrom(textualUploadDate), true);
+ return parseOptionalDate(dateText, "MMM dd, yyyy")
+ .or(() -> parseOptionalDate(dateText, "dd MMM yyyy"))
+ .map(date -> new DateWrapper(date.atStartOfDay(), true))
+ .orElseThrow(() ->
+ new ParsingException("Could not parse upload date \"" + dateText + "\""));
+ }
+
+ private Optional parseOptionalDate(final String date, final String pattern) {
+ try {
+ // TODO: this parses English formatted dates only, we need a better approach to parse
+ // the textual date
+ final var formatter = DateTimeFormatter.ofPattern(pattern, Locale.ENGLISH);
+ return Optional.of(LocalDate.parse(date, formatter));
+ } catch (final DateTimeParseException e) {
+ return Optional.empty();
+ }
}
@Nonnull
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamInfoItemExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamInfoItemExtractor.java
index 98730c39ed..7b4deaaa58 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamInfoItemExtractor.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamInfoItemExtractor.java
@@ -44,8 +44,8 @@
import javax.annotation.Nullable;
import java.time.Instant;
-import java.time.OffsetDateTime;
-import java.time.ZoneOffset;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.regex.Pattern;
@@ -247,12 +247,14 @@ public boolean isUploaderVerified() throws ParsingException {
@Nullable
@Override
public String getTextualUploadDate() throws ParsingException {
- if (getStreamType().equals(StreamType.LIVE_STREAM)) {
+ if (getStreamType() == StreamType.LIVE_STREAM) {
return null;
}
if (isPremiere()) {
- return DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").format(getDateFromPremiere());
+ final var localDateTime = LocalDateTime.ofInstant(getInstantFromPremiere(),
+ ZoneId.systemDefault());
+ return DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").format(localDateTime);
}
String publishedTimeText = getTextFromObject(videoInfo.getObject("publishedTimeText"));
@@ -273,12 +275,12 @@ public String getTextualUploadDate() throws ParsingException {
@Nullable
@Override
public DateWrapper getUploadDate() throws ParsingException {
- if (getStreamType().equals(StreamType.LIVE_STREAM)) {
+ if (getStreamType() == StreamType.LIVE_STREAM) {
return null;
}
if (isPremiere()) {
- return new DateWrapper(getDateFromPremiere());
+ return new DateWrapper(getInstantFromPremiere());
}
final String textualUploadDate = getTextualUploadDate();
@@ -402,15 +404,15 @@ private boolean isPremiere() {
return isPremiere;
}
- private OffsetDateTime getDateFromPremiere() throws ParsingException {
+ private Instant getInstantFromPremiere() throws ParsingException {
final JsonObject upcomingEventData = videoInfo.getObject("upcomingEventData");
final String startTime = upcomingEventData.getString("startTime");
try {
- return OffsetDateTime.ofInstant(Instant.ofEpochSecond(Long.parseLong(startTime)),
- ZoneOffset.UTC);
+ return Instant.ofEpochSecond(Long.parseLong(startTime));
} catch (final Exception e) {
- throw new ParsingException("Could not parse date from premiere: \"" + startTime + "\"");
+ final String message = "Could not parse date from premiere: \"" + startTime + "\"";
+ throw new ParsingException(message, e);
}
}
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamInfoItemLockupExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamInfoItemLockupExtractor.java
index f685f33dec..416b9a7258 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamInfoItemLockupExtractor.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamInfoItemLockupExtractor.java
@@ -18,7 +18,6 @@
import org.schabi.newpipe.extractor.utils.Utils;
import java.time.LocalDateTime;
-import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
@@ -292,8 +291,8 @@ public DateWrapper getUploadDate() throws ParsingException {
try {
// As we request a UTC offset of 0 minutes, we get the UTC date
- return new DateWrapper(OffsetDateTime.of(LocalDateTime.parse(
- premiereDate, PREMIERES_DATE_FORMATTER), ZoneOffset.UTC));
+ final var dateTime = LocalDateTime.parse(premiereDate, PREMIERES_DATE_FORMATTER);
+ return new DateWrapper(dateTime.atZone(ZoneOffset.UTC).toInstant(), false);
} catch (final DateTimeParseException e) {
throw new ParsingException("Could not parse premiere upload date", e);
}
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/kiosk/YoutubeChartsBaseKioskExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/kiosk/YoutubeChartsBaseKioskExtractor.java
index 3ede87619e..f5618a342a 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/kiosk/YoutubeChartsBaseKioskExtractor.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/kiosk/YoutubeChartsBaseKioskExtractor.java
@@ -27,7 +27,7 @@
import javax.annotation.Nullable;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
-import java.time.OffsetDateTime;
+import java.time.LocalDate;
import java.time.ZoneOffset;
import java.util.HashMap;
import java.util.List;
@@ -203,20 +203,15 @@ public String getTextualUploadDate() {
@Nonnull
@Override
public DateWrapper getUploadDate() {
- final JsonObject releaseDate = videoObject.getObject("releaseDate");
- return new DateWrapper(OffsetDateTime.of(
- releaseDate.getInt("year"),
- releaseDate.getInt("month"),
- releaseDate.getInt("day"),
- 0,
- 0,
- 0,
- 0,
- // We request that times should be returned with 0 offset to UTC timezone in
- // the JSON body, but YouTube charts does it only in its HTTP headers
- ZoneOffset.UTC),
- // We don't have more info than the release day
- true);
+ final var releaseDate = videoObject.getObject("releaseDate");
+ final var localDate = LocalDate.of(releaseDate.getInt("year"),
+ releaseDate.getInt("month"), releaseDate.getInt("day"));
+ // We request that times should be returned with 0 offset to UTC timezone in
+ // the JSON body, but YouTube charts does it only in its HTTP headers
+ final var instant = localDate.atStartOfDay(ZoneOffset.UTC).toInstant();
+
+ // We don't have more info than the release day, hence isApproximate=true
+ return new DateWrapper(instant, true);
}
@Override
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamExtractor.java
index f54ed177da..63650a7906 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamExtractor.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamExtractor.java
@@ -39,6 +39,7 @@
import javax.annotation.Nullable;
import java.io.IOException;
+import java.time.Instant;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
@@ -71,7 +72,7 @@ public String getTextualUploadDate() throws ParsingException {
}
/**
- * A more general {@code Calendar} instance set to the date provided by the service.
+ * A more general {@link Instant} instance set to the date provided by the service.
* Implementations usually will just parse the date returned from the {@link
* #getTextualUploadDate()}.
*
diff --git a/extractor/src/test/java/org/schabi/newpipe/extractor/localization/TimeAgoParserTest.java b/extractor/src/test/java/org/schabi/newpipe/extractor/localization/TimeAgoParserTest.java
index db45e807b9..451b66ac35 100644
--- a/extractor/src/test/java/org/schabi/newpipe/extractor/localization/TimeAgoParserTest.java
+++ b/extractor/src/test/java/org/schabi/newpipe/extractor/localization/TimeAgoParserTest.java
@@ -11,10 +11,8 @@
import java.time.Duration;
import java.time.LocalDateTime;
-import java.time.OffsetDateTime;
-import java.time.ZoneOffset;
+import java.time.Month;
import java.time.temporal.ChronoUnit;
-import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Stream;
@@ -41,13 +39,9 @@ public static Stream parseTimeAgo() {
@ParameterizedTest
@MethodSource
void parseTimeAgo(final ParseTimeAgoTestData testData) {
- final OffsetDateTime now = OffsetDateTime.of(
- LocalDateTime.of(2020, 1, 1, 1, 1, 1),
- ZoneOffset.UTC);
- final TimeAgoParser parser = Objects.requireNonNull(
- TimeAgoPatternsManager.getTimeAgoParserFor(Localization.DEFAULT, now));
-
- final OffsetDateTime expected = testData.getExpectedApplyToNow().apply(now);
+ final var now = LocalDateTime.of(2020, Month.JANUARY, 1, 1, 1, 1);
+ final var parser = TimeAgoPatternsManager.getTimeAgoParserFor(Localization.DEFAULT, now);
+ final var expected = testData.getExpectedApplyToNow().apply(now);
assertAll(
Stream.of(
@@ -55,7 +49,7 @@ void parseTimeAgo(final ParseTimeAgoTestData testData) {
testData.getTextualDateShort())
.map(textualDate -> () -> assertEquals(
expected,
- parser.parse(textualDate).offsetDateTime(),
+ parser.parse(textualDate).getLocalDateTime(),
"Expected " + expected + " for " + textualDate
))
);
@@ -63,12 +57,12 @@ void parseTimeAgo(final ParseTimeAgoTestData testData) {
static class ParseTimeAgoTestData {
public static final String AGO_SUFFIX = " ago";
- private final Function expectedApplyToNow;
+ private final Function expectedApplyToNow;
private final String textualDateLong;
private final String textualDateShort;
ParseTimeAgoTestData(
- final Function expectedApplyToNow,
+ final Function expectedApplyToNow,
final String textualDateLong,
final String textualDateShort
) {
@@ -89,17 +83,17 @@ public static ParseTimeAgoTestData lessThanDay(
}
public static ParseTimeAgoTestData greaterThanDay(
- final Function expectedApplyToNow,
+ final Function expectedApplyToNow,
final String textualDateLong,
final String textualDateShort
) {
return new ParseTimeAgoTestData(
- d -> expectedApplyToNow.apply(d).truncatedTo(ChronoUnit.HOURS),
+ expectedApplyToNow.andThen(d -> d.truncatedTo(ChronoUnit.DAYS)),
textualDateLong + AGO_SUFFIX,
textualDateShort + AGO_SUFFIX);
}
- public Function getExpectedApplyToNow() {
+ public Function getExpectedApplyToNow() {
return expectedApplyToNow;
}
diff --git a/extractor/src/test/java/org/schabi/newpipe/extractor/services/DefaultStreamExtractorTest.java b/extractor/src/test/java/org/schabi/newpipe/extractor/services/DefaultStreamExtractorTest.java
index 0d04157efc..b27343ee56 100644
--- a/extractor/src/test/java/org/schabi/newpipe/extractor/services/DefaultStreamExtractorTest.java
+++ b/extractor/src/test/java/org/schabi/newpipe/extractor/services/DefaultStreamExtractorTest.java
@@ -5,7 +5,6 @@
import org.schabi.newpipe.extractor.InfoItemsCollector;
import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.MetaInfo;
-import org.schabi.newpipe.extractor.localization.DateWrapper;
import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.ContentAvailability;
import org.schabi.newpipe.extractor.stream.Description;
@@ -17,6 +16,7 @@
import javax.annotation.Nullable;
import java.time.LocalDateTime;
+import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.net.MalformedURLException;
import java.net.URL;
@@ -195,18 +195,18 @@ public void testViewCount() throws Exception {
@Test
@Override
public void testUploadDate() throws Exception {
- final DateWrapper dateWrapper = extractor().getUploadDate();
+ final var dateWrapper = extractor().getUploadDate();
+ final var expectedDate = expectedUploadDate();
- if (expectedUploadDate() == null) {
+ if (expectedDate == null) {
assertNull(dateWrapper);
} else {
assertNotNull(dateWrapper);
- final LocalDateTime expectedDateTime = LocalDateTime.parse(expectedUploadDate(),
+ final var expectedDateTime = LocalDateTime.parse(expectedDate,
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));
- final LocalDateTime actualDateTime = dateWrapper.offsetDateTime().toLocalDateTime();
- assertEquals(expectedDateTime, actualDateTime);
+ assertEquals(expectedDateTime, dateWrapper.getLocalDateTime(ZoneOffset.UTC));
}
}
diff --git a/extractor/src/test/java/org/schabi/newpipe/extractor/services/bandcamp/BandcampRadioStreamExtractorTest.java b/extractor/src/test/java/org/schabi/newpipe/extractor/services/bandcamp/BandcampRadioStreamExtractorTest.java
index 78d490bd92..378da8053c 100644
--- a/extractor/src/test/java/org/schabi/newpipe/extractor/services/bandcamp/BandcampRadioStreamExtractorTest.java
+++ b/extractor/src/test/java/org/schabi/newpipe/extractor/services/bandcamp/BandcampRadioStreamExtractorTest.java
@@ -19,10 +19,11 @@
import org.schabi.newpipe.extractor.stream.StreamType;
import java.io.IOException;
-import java.util.Calendar;
+import java.time.LocalDate;
+import java.time.Month;
+import java.time.ZoneOffset;
import java.util.Collections;
import java.util.List;
-import java.util.TimeZone;
public class BandcampRadioStreamExtractorTest extends DefaultStreamExtractorTest {
@@ -82,14 +83,10 @@ public List expectedDescriptionContains() {
@Override
@Test
public void testUploadDate() throws ParsingException {
- final Calendar expectedCalendar = Calendar.getInstance();
-
- // 16 May 2017 00:00:00 GMT
- expectedCalendar.setTimeZone(TimeZone.getTimeZone("GMT"));
- expectedCalendar.setTimeInMillis(0);
- expectedCalendar.set(2017, Calendar.MAY, 16);
-
- assertEquals(expectedCalendar.getTimeInMillis(), extractor().getUploadDate().offsetDateTime().toInstant().toEpochMilli());
+ final var expectedDate = LocalDate.of(2017, Month.MAY, 16);
+ final var actualDate = extractor().getUploadDate().getLocalDateTime(ZoneOffset.UTC)
+ .toLocalDate();
+ assertEquals(expectedDate, actualDate);
}
@Test
diff --git a/extractor/src/test/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudStreamExtractorTest.java b/extractor/src/test/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudStreamExtractorTest.java
index d7fab32d1f..3d67efcadc 100644
--- a/extractor/src/test/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudStreamExtractorTest.java
+++ b/extractor/src/test/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudStreamExtractorTest.java
@@ -6,6 +6,7 @@
import static org.schabi.newpipe.extractor.ServiceList.SoundCloud;
import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.schabi.newpipe.extractor.ExtractorAsserts;
import org.schabi.newpipe.extractor.MediaFormat;
@@ -27,7 +28,8 @@
public class SoundcloudStreamExtractorTest {
private static final String SOUNDCLOUD = "https://soundcloud.com/";
- public static class SoundcloudGeoRestrictedTrack extends DefaultStreamExtractorTest {
+ @Nested
+ class SoundcloudGeoRestrictedTrack extends DefaultStreamExtractorTest {
private static final String ID = "one-touch";
private static final String UPLOADER = SOUNDCLOUD + "jessglynne";
private static final int TIMESTAMP = 0;
@@ -63,7 +65,7 @@ protected void fetchExtractor(final StreamExtractor extractor) throws Exception
@Override public long expectedTimestamp() { return TIMESTAMP; }
@Override public long expectedViewCountAtLeast() { return 43000; }
@Nullable @Override public String expectedUploadDate() { return "2019-05-16 16:28:45.000"; }
- @Nullable @Override public String expectedTextualUploadDate() { return "2019-05-16 16:28:45"; }
+ @Nullable @Override public String expectedTextualUploadDate() { return "2019-05-16T16:28:45Z"; }
@Override public long expectedLikeCountAtLeast() { return 600; }
@Override public long expectedDislikeCountAtLeast() { return -1; }
@Override public boolean expectedHasAudioStreams() { return false; }
@@ -82,7 +84,8 @@ public void testRelatedItems() throws Exception {
}
}
- public static class SoundcloudGoPlusTrack extends DefaultStreamExtractorTest {
+ @Nested
+ class SoundcloudGoPlusTrack extends DefaultStreamExtractorTest {
private static final String ID = "places";
private static final String UPLOADER = SOUNDCLOUD + "martinsolveig";
private static final int TIMESTAMP = 0;
@@ -127,7 +130,7 @@ public void testRelatedItems() throws Exception {
@Override public long expectedTimestamp() { return TIMESTAMP; }
@Override public long expectedViewCountAtLeast() { return 386000; }
@Nullable @Override public String expectedUploadDate() { return "2016-11-11 01:16:37.000"; }
- @Nullable @Override public String expectedTextualUploadDate() { return "2016-11-11 01:16:37"; }
+ @Nullable @Override public String expectedTextualUploadDate() { return "2016-11-11T01:16:37Z"; }
@Override public long expectedLikeCountAtLeast() { return 7350; }
@Override public long expectedDislikeCountAtLeast() { return -1; }
@Override public boolean expectedHasAudioStreams() { return false; }
@@ -139,7 +142,8 @@ public void testRelatedItems() throws Exception {
@Override public String expectedCategory() { return "Dance"; }
}
- static class CreativeCommonsOpenMindsEp21 extends DefaultStreamExtractorTest {
+ @Nested
+ class CreativeCommonsOpenMindsEp21 extends DefaultStreamExtractorTest {
private static final String ID = "open-minds-ep-21-dr-beth-harris-and-dr-steven-zucker-of-smarthistory";
private static final String UPLOADER = SOUNDCLOUD + "wearecc";
private static final int TIMESTAMP = 69;
@@ -167,7 +171,7 @@ protected StreamExtractor createExtractor() throws Exception {
@Override public long expectedTimestamp() { return TIMESTAMP; }
@Override public long expectedViewCountAtLeast() { return 15000; }
@Nullable @Override public String expectedUploadDate() { return "2022-10-03 18:49:49.000"; }
- @Nullable @Override public String expectedTextualUploadDate() { return "2022-10-03 18:49:49"; }
+ @Nullable @Override public String expectedTextualUploadDate() { return "2022-10-03T18:49:49Z"; }
@Override public long expectedLikeCountAtLeast() { return 10; }
@Override public long expectedDislikeCountAtLeast() { return -1; }
@Override public boolean expectedHasRelatedItems() { return false; }
diff --git a/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeChannelLocalizationTest.java b/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeChannelLocalizationTest.java
index 6633a76f89..cae661f8e1 100644
--- a/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeChannelLocalizationTest.java
+++ b/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeChannelLocalizationTest.java
@@ -73,9 +73,8 @@ private void testLocalizationsFor(final String channelUrl) throws Exception {
+ "\n:::: " + item.getStreamType() + ", views = " + item.getViewCount();
final DateWrapper uploadDate = item.getUploadDate();
if (uploadDate != null) {
- final String dateAsText = dateTimeFormatter.format(uploadDate.offsetDateTime());
- debugMessage += "\n:::: " + item.getTextualUploadDate() +
- "\n:::: " + dateAsText;
+ final String dateStr = dateTimeFormatter.format(uploadDate.getLocalDateTime());
+ debugMessage += "\n:::: " + item.getTextualUploadDate() + "\n:::: " + dateStr;
}
if (DEBUG) System.out.println(debugMessage + "\n");
}