All apt versions support numeric as well as 3-character timezones just
fine and its actually hard to write code which doesn't "accidently"
accepts it. So why change? Documenting the Date/Valid-Until fields in
the Release file is easy to do in terms of referencing the
datetime format used e.g. in the Debian changelogs (policy ยง4.4). This
format specifies only the numeric timezones through, not the nowadays
obsolete 3-character ones, so in the interest of least surprise we should
use the same format even through it carries a small risk of regression
in other clients (which encounter repositories created with
apt-ftparchive).
In case it is really regressing in practice, the hidden option
-o APT::FTPArchive::Release::NumericTimezone=0
can be used to go back to good old UTC as timezone.
The EDSP and EIPP protocols use this 'new' format, the text interface
used to communicate with the acquire methods does not for compatibility
reasons even if none of our methods would be effected and I doubt any
other would (in these instances the timezone is 'GMT' as that is what
HTTP/1.1 requires). Note that this is only true for apt talking to
methods, (libapt-based) methods talking to apt will respond with the
'new' format. It is therefore strongly adviced to support both also in
method input.
13 files changed:
string const FinalFile = GetFinalFilename();
struct stat Buf;
if (stat(FinalFile.c_str(),&Buf) == 0)
string const FinalFile = GetFinalFilename();
struct stat Buf;
if (stat(FinalFile.c_str(),&Buf) == 0)
- Header += "\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
+ Header += "\nLast-Modified: " + TimeRFC1123(Buf.st_mtime, false);
if (timespec == 0)
ErrorText.append("<unknown>");
else
if (timespec == 0)
ErrorText.append("<unknown>");
else
- ErrorText.append(TimeRFC1123(timespec));
+ ErrorText.append(TimeRFC1123(timespec, true));
ErrorText.append("\n");
}
/*}}}*/
ErrorText.append("\n");
}
/*}}}*/
if (stat(Final.c_str(),&Buf) != 0)
return "\nIndex-File: true";
if (stat(Final.c_str(),&Buf) != 0)
return "\nIndex-File: true";
- return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
+ return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime, false);
}
/*}}}*/
void pkgAcqDiffIndex::QueueOnIMSHit() const /*{{{*/
}
/*}}}*/
void pkgAcqDiffIndex::QueueOnIMSHit() const /*{{{*/
struct stat Buf;
if (stat(Final.c_str(),&Buf) == 0)
struct stat Buf;
if (stat(Final.c_str(),&Buf) == 0)
- msg += "\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
+ msg += "\nLast-Modified: " + TimeRFC1123(Buf.st_mtime, false);
std::cout << "Size: " << std::to_string(Res.Size) << "\n";
if (Res.LastModified != 0)
std::cout << "Size: " << std::to_string(Res.Size) << "\n";
if (Res.LastModified != 0)
- std::cout << "Last-Modified: " << TimeRFC1123(Res.LastModified) << "\n";
+ std::cout << "Last-Modified: " << TimeRFC1123(Res.LastModified, true) << "\n";
if (Res.ResumePoint != 0)
std::cout << "Resume-Point: " << std::to_string(Res.ResumePoint) << "\n";
if (Res.ResumePoint != 0)
std::cout << "Resume-Point: " << std::to_string(Res.ResumePoint) << "\n";
std::cout << "Size: " << std::to_string(Res.Size) << "\n";
if (Res.LastModified != 0)
std::cout << "Size: " << std::to_string(Res.Size) << "\n";
if (Res.LastModified != 0)
- std::cout << "Last-Modified: " << TimeRFC1123(Res.LastModified) << "\n";
+ std::cout << "Last-Modified: " << TimeRFC1123(Res.LastModified, true) << "\n";
printHashStringList(&Res.Hashes);
printHashStringList(&Res.Hashes);
std::cout << "Alt-Size: " << std::to_string(Alt->Size) << "\n";
if (Alt->LastModified != 0)
std::cout << "Alt-Size: " << std::to_string(Alt->Size) << "\n";
if (Alt->LastModified != 0)
- std::cout << "Alt-Last-Modified: " << TimeRFC1123(Alt->LastModified) << "\n";
+ std::cout << "Alt-Last-Modified: " << TimeRFC1123(Alt->LastModified, true) << "\n";
printHashStringList(&Alt->Hashes);
printHashStringList(&Alt->Hashes);
/* This converts a time_t into a string time representation that is
year 2000 complient and timezone neutral */
string TimeRFC1123(time_t Date)
/* This converts a time_t into a string time representation that is
year 2000 complient and timezone neutral */
string TimeRFC1123(time_t Date)
+{
+ return TimeRFC1123(Date, false);
+}
+string TimeRFC1123(time_t Date, bool const NumericTimezone)
{
struct tm Conv;
if (gmtime_r(&Date, &Conv) == NULL)
{
struct tm Conv;
if (gmtime_r(&Date, &Conv) == NULL)
auto const posix = std::locale("C.UTF-8");
std::ostringstream datestr;
datestr.imbue(posix);
auto const posix = std::locale("C.UTF-8");
std::ostringstream datestr;
datestr.imbue(posix);
- APT::StringView const fmt("%a, %d %b %Y %H:%M:%S GMT");
+ APT::StringView const fmt("%a, %d %b %Y %H:%M:%S");
std::use_facet<std::time_put<char>>(posix).put(
std::ostreambuf_iterator<char>(datestr),
datestr, ' ', &Conv, fmt.data(), fmt.data() + fmt.size());
std::use_facet<std::time_put<char>>(posix).put(
std::ostreambuf_iterator<char>(datestr),
datestr, ' ', &Conv, fmt.data(), fmt.data() + fmt.size());
+ if (NumericTimezone)
+ datestr << " +0000";
+ else
+ datestr << " GMT";
return datestr.str();
}
/*}}}*/
return datestr.str();
}
/*}}}*/
std::string Base64Encode(const std::string &Str);
std::string OutputInDepth(const unsigned long Depth, const char* Separator=" ");
std::string URItoFileName(const std::string &URI);
std::string Base64Encode(const std::string &Str);
std::string OutputInDepth(const unsigned long Depth, const char* Separator=" ");
std::string URItoFileName(const std::string &URI);
-std::string TimeRFC1123(time_t Date);
+APT_DEPRECATED_MSG("Specify if GMT is required or a numeric timezone can be used") std::string TimeRFC1123(time_t Date);
+/** returns a datetime string as needed by HTTP/1.1 and Debian files.
+ *
+ * Note: The date will always be represented in a UTC timezone
+ *
+ * @param Date to be represented as a string
+ * @param NumericTimezone is preferred in general, but HTTP/1.1 requires the use
+ * of GMT as timezone instead. \b true means that the timezone should be denoted
+ * as "+0000" while \b false uses "GMT".
+ */
+std::string TimeRFC1123(time_t Date, bool const NumericTimezone);
/** parses time as needed by HTTP/1.1 and Debian files.
*
* HTTP/1.1 prefers dates in RFC1123 format (but the other two obsolete date formats
/** parses time as needed by HTTP/1.1 and Debian files.
*
* HTTP/1.1 prefers dates in RFC1123 format (but the other two obsolete date formats
/*}}}*/
// EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
bool EDSP::WriteProgress(unsigned short const percent, const char* const message, FILE* output) {
/*}}}*/
// EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
bool EDSP::WriteProgress(unsigned short const percent, const char* const message, FILE* output) {
- fprintf(output, "Progress: %s\n", TimeRFC1123(time(NULL)).c_str());
+ fprintf(output, "Progress: %s\n", TimeRFC1123(time(NULL), true).c_str());
fprintf(output, "Percentage: %d\n", percent);
fprintf(output, "Message: %s\n\n", message);
fflush(output);
return true;
}
bool EDSP::WriteProgress(unsigned short const percent, const char* const message, FileFd &output) {
fprintf(output, "Percentage: %d\n", percent);
fprintf(output, "Message: %s\n\n", message);
fflush(output);
return true;
}
bool EDSP::WriteProgress(unsigned short const percent, const char* const message, FileFd &output) {
- return WriteOkay(output, "Progress: ", TimeRFC1123(time(NULL)), "\n",
+ return WriteOkay(output, "Progress: ", TimeRFC1123(time(NULL), true), "\n",
"Percentage: ", percent, "\n",
"Message: ", message, "\n\n") && output.Flush();
}
"Percentage: ", percent, "\n",
"Message: ", message, "\n\n") && output.Flush();
}
std::cout << " Size: " << F->Size << std::endl;
std::cout << " ID: " << F->ID << std::endl;
std::cout << " Flags: " << F->Flags << std::endl;
std::cout << " Size: " << F->Size << std::endl;
std::cout << " ID: " << F->ID << std::endl;
std::cout << " Flags: " << F->Flags << std::endl;
- std::cout << " Time: " << TimeRFC1123(F->mtime) << std::endl;
+ std::cout << " Time: " << TimeRFC1123(F->mtime, true) << std::endl;
std::cout << " Archive: " << DeNull(F.Archive()) << std::endl;
std::cout << " Component: " << DeNull(F.Component()) << std::endl;
std::cout << " Version: " << DeNull(F.Version()) << std::endl;
std::cout << " Archive: " << DeNull(F.Archive()) << std::endl;
std::cout << " Component: " << DeNull(F.Component()) << std::endl;
std::cout << " Version: " << DeNull(F.Version()) << std::endl;
with the Progress field and might contain the following fields:
- **Progress:** (mandatory). The value of this field is a date and time
with the Progress field and might contain the following fields:
- **Progress:** (mandatory). The value of this field is a date and time
- timestamp, in RFC 2822 format. The timestamp provides a time
- annotation for the progress report.
+ timestamp from the UTC timezone, in RFC 2822 format (see 'date -uR' as
+ an example). The timestamp provides a time annotation for the
+ progress report.
- **Percentage:** (optional). An integer from 0 to 100, representing the
completion of the dependency solving process, as declared by the
- **Percentage:** (optional). An integer from 0 to 100, representing the
completion of the dependency solving process, as declared by the
with the Progress field and might contain the following fields:
- **Progress:** (mandatory). The value of this field is a date and time
with the Progress field and might contain the following fields:
- **Progress:** (mandatory). The value of this field is a date and time
- timestamp, in RFC 2822 format. The timestamp provides a time
- annotation for the progress report.
+ timestamp from the UTC timezone, in RFC 2822 format (see 'date -uR' as
+ an example). The timestamp provides a time annotation for the
+ progress report.
- **Percentage:** (optional). An integer from 0 to 100, representing the
completion of the installation planning process, as declared by the
- **Percentage:** (optional). An integer from 0 to 100, representing the
completion of the installation planning process, as declared by the
/* */
static std::string formatUTCDateTime(time_t const now)
{
/* */
static std::string formatUTCDateTime(time_t const now)
{
+ bool const NumericTimezone = _config->FindB("APT::FTPArchive::Release::NumericTimezone", true);
// TimeRFC1123 uses GMT to satisfy HTTP/1.1
// TimeRFC1123 uses GMT to satisfy HTTP/1.1
- std::string datetime = TimeRFC1123(now);
- auto const lastspace = datetime.rfind(' ');
- if (likely(lastspace != std::string::npos))
- datetime.replace(lastspace + 1, 3, "UTC");
+ std::string datetime = TimeRFC1123(now, NumericTimezone);
+ if (NumericTimezone == false)
+ {
+ auto const lastspace = datetime.rfind(' ');
+ if (likely(lastspace != std::string::npos))
+ datetime.replace(lastspace + 1, 3, "UTC");
+ }
return datetime;
}
ReleaseWriter::ReleaseWriter(FileFd * const GivenOutput, string const &/*DB*/) : FTWScanner(GivenOutput)
return datetime;
}
ReleaseWriter::ReleaseWriter(FileFd * const GivenOutput, string const &/*DB*/) : FTWScanner(GivenOutput)
struct stat SBuf;
if (stat(Itm->DestFile.c_str(),&SBuf) >= 0 && SBuf.st_size > 0)
Req << "Range: bytes=" << SBuf.st_size << "-\r\n"
struct stat SBuf;
if (stat(Itm->DestFile.c_str(),&SBuf) >= 0 && SBuf.st_size > 0)
Req << "Range: bytes=" << SBuf.st_size << "-\r\n"
- << "If-Range: " << TimeRFC1123(SBuf.st_mtime) << "\r\n";
+ << "If-Range: " << TimeRFC1123(SBuf.st_mtime, false) << "\r\n";
else if (Itm->LastModified != 0)
else if (Itm->LastModified != 0)
- Req << "If-Modified-Since: " << TimeRFC1123(Itm->LastModified).c_str() << "\r\n";
+ Req << "If-Modified-Since: " << TimeRFC1123(Itm->LastModified, false).c_str() << "\r\n";
if (Server->Proxy.User.empty() == false || Server->Proxy.Password.empty() == false)
Req << "Proxy-Authorization: Basic "
if (Server->Proxy.User.empty() == false || Server->Proxy.Password.empty() == false)
Req << "Proxy-Authorization: Basic "
std::string Buf;
strprintf(Buf, "Range: bytes=%lli-", (long long) SBuf.st_size);
headers = curl_slist_append(headers, Buf.c_str());
std::string Buf;
strprintf(Buf, "Range: bytes=%lli-", (long long) SBuf.st_size);
headers = curl_slist_append(headers, Buf.c_str());
- strprintf(Buf, "If-Range: %s", TimeRFC1123(SBuf.st_mtime).c_str());
+ strprintf(Buf, "If-Range: %s", TimeRFC1123(SBuf.st_mtime, false).c_str());
headers = curl_slist_append(headers, Buf.c_str());
}
else if(Itm->LastModified > 0)
headers = curl_slist_append(headers, Buf.c_str());
}
else if(Itm->LastModified > 0)
runapt command gdb --quiet -ex run "$CMD" --args "$CMD" "$@"
}
lastmodification() {
runapt command gdb --quiet -ex run "$CMD" --args "$CMD" "$@"
}
lastmodification() {
- date -u -d "@$(stat -c '%Y' "${TMPWORKINGDIRECTORY}/$1")" '+%a, %d %b %Y %H:%M:%S GMT'
+ date -u -d "@$(stat -c '%Y' "${TMPWORKINGDIRECTORY}/$1")" -R
- grep "^${2:-Date}:" "$1" | cut -d' ' -f 2- | sed -e 's#UTC#GMT#'
+ grep "^${2:-Date}:" "$1" | cut -d' ' -f 2-
fi
if [ -n "$DATE" -a "$DATE" != "now" ]; then
for release in $(find ./aptarchive -name 'Release'); do
fi
if [ -n "$DATE" -a "$DATE" != "now" ]; then
for release in $(find ./aptarchive -name 'Release'); do
- sed -i "s/^Date: .*$/Date: $(date -u -d "$DATE" '+%a, %d %b %Y %H:%M:%S %Z')/" "$release"
+ sed -i "s/^Date: .*$/Date: $(date -u -d "$DATE" -R)/" "$release"
touch -d "$DATE" "$release"
done
fi
if [ -n "$VALIDUNTIL" ]; then
sed -i "/^Date: / a\
touch -d "$DATE" "$release"
done
fi
if [ -n "$VALIDUNTIL" ]; then
sed -i "/^Date: / a\
-Valid-Until: $(date -u -d "$VALIDUNTIL" '+%a, %d %b %Y %H:%M:%S %Z')" $(find ./aptarchive -name 'Release')
+Valid-Until: $(date -u -d "$VALIDUNTIL" -R)" $(find ./aptarchive -name 'Release')
- local DATE="$(date -u -d "$1" '+%a, %d %b %Y %H:%M:%S %Z')"
+ local DATE="$(date -u -d "$1" -R)"
for release in $(find aptarchive/ -name 'Release'); do
sed -i "s/^Date: .*$/Date: ${DATE}/" "$release"
touch -d "$DATE" "$release"
for release in $(find aptarchive/ -name 'Release'); do
sed -i "s/^Date: .*$/Date: ${DATE}/" "$release"
touch -d "$DATE" "$release"
if (_config->FindB("aptwebserver::support::last-modified", true) == true)
{
std::string lastmodified("Last-Modified: ");
if (_config->FindB("aptwebserver::support::last-modified", true) == true)
{
std::string lastmodified("Last-Modified: ");
- lastmodified.append(TimeRFC1123(data.ModificationTime()));
+ lastmodified.append(TimeRFC1123(data.ModificationTime(), false));
headers.push_back(lastmodified);
}
}
headers.push_back(lastmodified);
}
}
headers.push_back(*h);
std::string date("Date: ");
headers.push_back(*h);
std::string date("Date: ");
- date.append(TimeRFC1123(time(NULL)));
+ date.append(TimeRFC1123(time(NULL), false));
headers.push_back(date);
if (chunkedTransferEncoding(headers) == true)
headers.push_back(date);
if (chunkedTransferEncoding(headers) == true)
<< "<td><a href=\"" << namelist[i]->d_name << "\">" << namelist[i]->d_name << "</a></td>"
<< "<td>" << SizeToStr(fs.st_size) << "B</td>";
}
<< "<td><a href=\"" << namelist[i]->d_name << "\">" << namelist[i]->d_name << "</a></td>"
<< "<td>" << SizeToStr(fs.st_size) << "B</td>";
}
- listing << "<td>" << TimeRFC1123(fs.st_mtime) << "</td></tr>" << std::endl;
+ listing << "<td>" << TimeRFC1123(fs.st_mtime, true) << "</td></tr>" << std::endl;
}
listing << "</table></body></html>" << std::endl;
}
listing << "</table></body></html>" << std::endl;