635 int unknown[3] = {-1, -1, -1};
642 static const QRegularExpression timeRx(
643 u"(\\d\\d?):(\\d\\d?)(?::(\\d\\d?)(?:\\.(\\d{1,3}))?)?(?:\\s*(am|pm))?"_s);
646 while (at < dateString.size()) {
647#ifdef PARSEDATESTRINGDEBUG
648 qDebug() << dateString.mid(at);
650 bool isNum = isNumber(dateString[at]);
654 && checkStaticArray(month, dateString, at, months,
sizeof(months)- 1)) {
656#ifdef PARSEDATESTRINGDEBUG
657 qDebug() <<
"Month:" << month;
665 && checkStaticArray(zoneOffset, dateString, at, zones,
sizeof(zones)- 1)) {
666 int sign = (at >= 0 && dateString[at - 1] ==
'-') ? -1 : 1;
667 zoneOffset = sign *
zoneOffsets[zoneOffset] * 60 * 60;
668#ifdef PARSEDATESTRINGDEBUG
669 qDebug() <<
"Zone:" << month;
676 && (zoneOffset == -1 || zoneOffset == 0)
677 && (dateString[at] ==
'+' || dateString[at] ==
'-')
679 || isWhitespace(dateString[at - 1])
680 || dateString[at - 1] ==
','
682 && (dateString[at - 3] ==
'g')
683 && (dateString[at - 2] ==
'm')
684 && (dateString[at - 1] ==
't')))) {
687 while (end < 5 && dateString.size() > at+end
688 && dateString[at + end] >=
'0' && dateString[at + end] <=
'9')
694 minutes = dateString.mid(at + 3, 2).toInt();
697 hours = dateString.mid(at + 1, 2).toInt();
700 hours = dateString.mid(at + 1, 1).toInt();
707 int sign = dateString[at] ==
'-' ? -1 : 1;
708 zoneOffset = sign * ((minutes * 60) + (hours * 60 * 60));
709#ifdef PARSEDATESTRINGDEBUG
710 qDebug() <<
"Zone offset:" << zoneOffset << hours << minutes;
718 if (isNum && time.isNull()
719 && dateString.size() >= at + 3
720 && (dateString[at + 2] ==
':' || dateString[at + 1] ==
':')) {
725 QString dateToString = QString::fromLatin1(dateString);
726 if (
auto match = timeRx.match(dateToString, at); match.hasMatch()) {
727 int h = match.capturedView(1).toInt();
728 int m = match.capturedView(2).toInt();
729 int s = match.capturedView(3).toInt();
730 int ms = match.capturedView(4).toInt();
731 QStringView ampm = match.capturedView(5);
732 if (h < 12 && !ampm.isEmpty())
735 time = QTime(h, m, s, ms);
736#ifdef PARSEDATESTRINGDEBUG
737 qDebug() <<
"Time:" << match.capturedTexts() << match.capturedLength();
739 at += match.capturedLength();
747 && dateString.size() > at + 3) {
748 if (isNumber(dateString[at + 1])
749 && isNumber(dateString[at + 2])
750 && isNumber(dateString[at + 3])) {
751 year = dateString.mid(at, 4).toInt();
753#ifdef PARSEDATESTRINGDEBUG
754 qDebug() <<
"Year:" << year;
764 if (dateString.size() > at + 1
765 && isNumber(dateString[at + 1]))
767 int x = dateString.mid(at, length).toInt();
768 if (year == -1 && (x > 31 || x == 0)) {
771 if (unknown[0] == -1) unknown[0] = x;
772 else if (unknown[1] == -1) unknown[1] = x;
773 else if (unknown[2] == -1) unknown[2] = x;
776#ifdef PARSEDATESTRINGDEBUG
777 qDebug() <<
"Saving" << x;
789 int couldBe[3] = { 0, 0, 0 };
790 int unknownCount = 3;
791 for (
int i = 0; i < unknownCount; ++i) {
792 if (unknown[i] == -1) {
801 if (month == -1 && unknown[i] >= 1 && unknown[i] <= 12)
814 for (
int i = 0; i < unknownCount; ++i) {
815 int currentValue = unknown[i];
816 bool findMatchingMonth = couldBe[i] &
ADAY && currentValue >= 29;
817 bool findMatchingDay = couldBe[i] &
AMONTH;
818 if (!findMatchingMonth || !findMatchingDay)
820 for (
int j = 0; j < 3; ++j) {
823 for (
int k = 0; k < 2; ++k) {
824 if (k == 0 && !(findMatchingMonth && (couldBe[j] &
AMONTH)))
826 else if (k == 1 && !(findMatchingDay && (couldBe[j] &
ADAY)))
828 int m = currentValue;
832 if (m == -1) m = month;
842 case 4:
case 6:
case 9:
case 11:
847 if (d > 0 && d <= 31)
850 if (k == 0) findMatchingMonth = found;
851 else if (k == 1) findMatchingDay = found;
854 if (findMatchingMonth)
862 for (
int i = 0; i < unknownCount; ++i) {
864 for (
int j = 0; j < 3; ++j) {
865 if (couldBe[j] ==
ADAY && day == -1) {
868 }
else if (couldBe[j] ==
AMONTH && month == -1) {
871 }
else if (couldBe[j] ==
AYEAR && year == -1) {
878 couldBe[j] &= ~unset;
883 for (
int i = 0; i < unknownCount; ++i) {
884 if (couldBe[i] &
AMONTH && month == -1) month = unknown[i];
885 else if (couldBe[i] &
ADAY && day == -1) day = unknown[i];
886 else if (couldBe[i] &
AYEAR && year == -1) year = unknown[i];
888#ifdef PARSEDATESTRINGDEBUG
889 qDebug() <<
"Final set" << year << month << day;
892 if (year == -1 || month == -1 || day == -1) {
893#ifdef PARSEDATESTRINGDEBUG
894 qDebug() <<
"Parser failure" << year << month << day;
906 QDate date(year + y2k, month, day);
914 date = QDate(day + y2k, month, year);
916 QDateTime dateTime(date, time, QTimeZone::UTC);
918 if (zoneOffset != -1)
919 dateTime = dateTime.addSecs(zoneOffset);
921 if (!dateTime.isValid())
948QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(QByteArrayView cookieString)
959 QList<QNetworkCookie> result;
960 const QDateTime now = QDateTime::currentDateTimeUtc();
963 const int length = cookieString.size();
964 while (position < length) {
965 QNetworkCookie cookie;
968 std::pair<QByteArray,QByteArray> field = nextField(cookieString, position,
true);
969 if (field.first.isEmpty())
972 cookie.setName(field.first);
973 cookie.setValue(field.second);
975 position = nextNonWhitespace(cookieString, position);
976 while (position < length) {
977 switch (cookieString.at(position++)) {
980 field = nextField(cookieString, position,
false);
982 if (field.first.compare(
"expires", Qt::CaseInsensitive) == 0) {
983 position -= field.second.size();
985 for (end = position; end < length; ++end)
986 if (isValueSeparator(cookieString.at(end)))
989 QByteArray dateString = cookieString.mid(position, end - position).trimmed().toByteArray().toLower();
991 QDateTime dt = parseDateString(dateString);
993 cookie.setExpirationDate(dt);
995 }
else if (field.first.compare(
"domain", Qt::CaseInsensitive) == 0) {
996 QByteArrayView rawDomain = field.second;
998 if (!rawDomain.isEmpty()) {
999 QLatin1StringView maybeLeadingDot;
1000 if (rawDomain.startsWith(
'.')) {
1001 maybeLeadingDot =
"."_L1;
1002 rawDomain = rawDomain.mid(1);
1006 QString normalizedDomain = QUrl::fromAce(QUrl::toAce(QString::fromUtf8(rawDomain)));
1007 if (!normalizedDomain.isEmpty()) {
1008 cookie.setDomain(maybeLeadingDot + normalizedDomain);
1016 }
else if (field.first.compare(
"max-age", Qt::CaseInsensitive) == 0) {
1018 int secs = field.second.toInt(&ok);
1022 cookie.setExpirationDate(QDateTime::fromSecsSinceEpoch(0));
1024 cookie.setExpirationDate(now.addSecs(secs));
1028 }
else if (field.first.compare(
"path", Qt::CaseInsensitive) == 0) {
1029 if (field.second.startsWith(
'/')) {
1032 cookie.setPath(QString::fromUtf8(field.second));
1036 cookie.setPath(QString());
1038 }
else if (field.first.compare(
"secure", Qt::CaseInsensitive) == 0) {
1039 cookie.setSecure(
true);
1040 }
else if (field.first.compare(
"httponly", Qt::CaseInsensitive) == 0) {
1041 cookie.setHttpOnly(
true);
1042 }
else if (field.first.compare(
"samesite", Qt::CaseInsensitive) == 0) {
1043 cookie.setSameSitePolicy(sameSiteFromRawString(field.second));
1048 position = nextNonWhitespace(cookieString, position);
1052 if (!cookie.name().isEmpty())