634 int unknown[3] = {-1, -1, -1};
641 static const QRegularExpression timeRx(
642 u"(\\d\\d?):(\\d\\d?)(?::(\\d\\d?)(?:\\.(\\d{1,3}))?)?(?:\\s*(am|pm))?"_s);
645 while (at < dateString.size()) {
646#ifdef PARSEDATESTRINGDEBUG
647 qDebug() << dateString.mid(at);
649 bool isNum = isNumber(dateString[at]);
653 && checkStaticArray(month, dateString, at, months,
sizeof(months)- 1)) {
655#ifdef PARSEDATESTRINGDEBUG
656 qDebug() <<
"Month:" << month;
664 && checkStaticArray(zoneOffset, dateString, at, zones,
sizeof(zones)- 1)) {
665 int sign = (at >= 0 && dateString[at - 1] ==
'-') ? -1 : 1;
666 zoneOffset = sign *
zoneOffsets[zoneOffset] * 60 * 60;
667#ifdef PARSEDATESTRINGDEBUG
668 qDebug() <<
"Zone:" << month;
675 && (zoneOffset == -1 || zoneOffset == 0)
676 && (dateString[at] ==
'+' || dateString[at] ==
'-')
678 || isWhitespace(dateString[at - 1])
679 || dateString[at - 1] ==
','
681 && (dateString[at - 3] ==
'g')
682 && (dateString[at - 2] ==
'm')
683 && (dateString[at - 1] ==
't')))) {
686 while (end < 5 && dateString.size() > at+end
687 && dateString[at + end] >=
'0' && dateString[at + end] <=
'9')
693 minutes = dateString.mid(at + 3, 2).toInt();
696 hours = dateString.mid(at + 1, 2).toInt();
699 hours = dateString.mid(at + 1, 1).toInt();
706 int sign = dateString[at] ==
'-' ? -1 : 1;
707 zoneOffset = sign * ((minutes * 60) + (hours * 60 * 60));
708#ifdef PARSEDATESTRINGDEBUG
709 qDebug() <<
"Zone offset:" << zoneOffset << hours << minutes;
717 if (isNum && time.isNull()
718 && dateString.size() >= at + 3
719 && (dateString[at + 2] ==
':' || dateString[at + 1] ==
':')) {
724 QString dateToString = QString::fromLatin1(dateString);
725 if (
auto match = timeRx.match(dateToString, at); match.hasMatch()) {
726 int h = match.capturedView(1).toInt();
727 int m = match.capturedView(2).toInt();
728 int s = match.capturedView(3).toInt();
729 int ms = match.capturedView(4).toInt();
730 QStringView ampm = match.capturedView(5);
731 if (h < 12 && !ampm.isEmpty())
734 time = QTime(h, m, s, ms);
735#ifdef PARSEDATESTRINGDEBUG
736 qDebug() <<
"Time:" << match.capturedTexts() << match.capturedLength();
738 at += match.capturedLength();
746 && dateString.size() > at + 3) {
747 if (isNumber(dateString[at + 1])
748 && isNumber(dateString[at + 2])
749 && isNumber(dateString[at + 3])) {
750 year = dateString.mid(at, 4).toInt();
752#ifdef PARSEDATESTRINGDEBUG
753 qDebug() <<
"Year:" << year;
763 if (dateString.size() > at + 1
764 && isNumber(dateString[at + 1]))
766 int x = dateString.mid(at, length).toInt();
767 if (year == -1 && (x > 31 || x == 0)) {
770 if (unknown[0] == -1) unknown[0] = x;
771 else if (unknown[1] == -1) unknown[1] = x;
772 else if (unknown[2] == -1) unknown[2] = x;
775#ifdef PARSEDATESTRINGDEBUG
776 qDebug() <<
"Saving" << x;
788 int couldBe[3] = { 0, 0, 0 };
789 int unknownCount = 3;
790 for (
int i = 0; i < unknownCount; ++i) {
791 if (unknown[i] == -1) {
800 if (month == -1 && unknown[i] >= 1 && unknown[i] <= 12)
813 for (
int i = 0; i < unknownCount; ++i) {
814 int currentValue = unknown[i];
815 bool findMatchingMonth = couldBe[i] &
ADAY && currentValue >= 29;
816 bool findMatchingDay = couldBe[i] &
AMONTH;
817 if (!findMatchingMonth || !findMatchingDay)
819 for (
int j = 0; j < 3; ++j) {
822 for (
int k = 0; k < 2; ++k) {
823 if (k == 0 && !(findMatchingMonth && (couldBe[j] &
AMONTH)))
825 else if (k == 1 && !(findMatchingDay && (couldBe[j] &
ADAY)))
827 int m = currentValue;
831 if (m == -1) m = month;
841 case 4:
case 6:
case 9:
case 11:
846 if (d > 0 && d <= 31)
849 if (k == 0) findMatchingMonth = found;
850 else if (k == 1) findMatchingDay = found;
853 if (findMatchingMonth)
861 for (
int i = 0; i < unknownCount; ++i) {
863 for (
int j = 0; j < 3; ++j) {
864 if (couldBe[j] ==
ADAY && day == -1) {
867 }
else if (couldBe[j] ==
AMONTH && month == -1) {
870 }
else if (couldBe[j] ==
AYEAR && year == -1) {
877 couldBe[j] &= ~unset;
882 for (
int i = 0; i < unknownCount; ++i) {
883 if (couldBe[i] &
AMONTH && month == -1) month = unknown[i];
884 else if (couldBe[i] &
ADAY && day == -1) day = unknown[i];
885 else if (couldBe[i] &
AYEAR && year == -1) year = unknown[i];
887#ifdef PARSEDATESTRINGDEBUG
888 qDebug() <<
"Final set" << year << month << day;
891 if (year == -1 || month == -1 || day == -1) {
892#ifdef PARSEDATESTRINGDEBUG
893 qDebug() <<
"Parser failure" << year << month << day;
905 QDate date(year + y2k, month, day);
913 date = QDate(day + y2k, month, year);
915 QDateTime dateTime(date, time, QTimeZone::UTC);
917 if (zoneOffset != -1)
918 dateTime = dateTime.addSecs(zoneOffset);
920 if (!dateTime.isValid())
947QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(QByteArrayView cookieString)
958 QList<QNetworkCookie> result;
959 const QDateTime now = QDateTime::currentDateTimeUtc();
962 const int length = cookieString.size();
963 while (position < length) {
964 QNetworkCookie cookie;
967 std::pair<QByteArray,QByteArray> field = nextField(cookieString, position,
true);
968 if (field.first.isEmpty())
971 cookie.setName(field.first);
972 cookie.setValue(field.second);
974 position = nextNonWhitespace(cookieString, position);
975 while (position < length) {
976 switch (cookieString.at(position++)) {
979 field = nextField(cookieString, position,
false);
981 if (field.first.compare(
"expires", Qt::CaseInsensitive) == 0) {
982 position -= field.second.size();
984 for (end = position; end < length; ++end)
985 if (isValueSeparator(cookieString.at(end)))
988 QByteArray dateString = cookieString.mid(position, end - position).trimmed().toByteArray().toLower();
990 QDateTime dt = parseDateString(dateString);
992 cookie.setExpirationDate(dt);
994 }
else if (field.first.compare(
"domain", Qt::CaseInsensitive) == 0) {
995 QByteArrayView rawDomain = field.second;
997 if (!rawDomain.isEmpty()) {
998 QLatin1StringView maybeLeadingDot;
999 if (rawDomain.startsWith(
'.')) {
1000 maybeLeadingDot =
"."_L1;
1001 rawDomain = rawDomain.mid(1);
1005 QString normalizedDomain = QUrl::fromAce(QUrl::toAce(QString::fromUtf8(rawDomain)));
1006 if (!normalizedDomain.isEmpty()) {
1007 cookie.setDomain(maybeLeadingDot + normalizedDomain);
1015 }
else if (field.first.compare(
"max-age", Qt::CaseInsensitive) == 0) {
1017 int secs = field.second.toInt(&ok);
1021 cookie.setExpirationDate(QDateTime::fromSecsSinceEpoch(0));
1023 cookie.setExpirationDate(now.addSecs(secs));
1027 }
else if (field.first.compare(
"path", Qt::CaseInsensitive) == 0) {
1028 if (field.second.startsWith(
'/')) {
1031 cookie.setPath(QString::fromUtf8(field.second));
1035 cookie.setPath(QString());
1037 }
else if (field.first.compare(
"secure", Qt::CaseInsensitive) == 0) {
1038 cookie.setSecure(
true);
1039 }
else if (field.first.compare(
"httponly", Qt::CaseInsensitive) == 0) {
1040 cookie.setHttpOnly(
true);
1041 }
else if (field.first.compare(
"samesite", Qt::CaseInsensitive) == 0) {
1042 cookie.setSameSitePolicy(sameSiteFromRawString(field.second));
1047 position = nextNonWhitespace(cookieString, position);
1051 if (!cookie.name().isEmpty())