Repository :
https://github.com/FarGroup/FarManager
On branch : master
Link :
https://github.com/FarGroup/FarManager/commit/4d5036232042deaa2cf477a4fc228f8354972656
>---------------------------------------------------------------
commit 4d5036232042deaa2cf477a4fc228f8354972656
Author: Alex Alabuzhev <
alab...@gmail.com>
Date: Mon Apr 6 19:37:17 2026 +0100
Refactoring
>---------------------------------------------------------------
4d5036232042deaa2cf477a4fc228f8354972656
far/color_picker.cpp | 4 -
far/copy.cpp | 6 +-
far/datetime.cpp | 143 +++++++++++++++++------------
far/datetime.hpp | 4 +-
far/exception_handler.cpp | 2 +-
far/file_io.cpp | 2 +-
far/fileattr.cpp | 15 ++-
far/fileattr.hpp | 2 +-
far/filefilterparams.cpp | 21 +++--
far/history.cpp | 4 +-
far/imports.hpp | 2 +
far/interf.cpp | 2 +-
far/locale.cpp | 2 +-
far/macroapi.cpp | 2 +-
far/panelmix.cpp | 2 +-
far/platform.chrono.cpp | 227 +++++++++++++++++++++++++++++++++-------------
far/platform.chrono.hpp | 7 +-
far/platform.fs.cpp | 23 +++--
far/platform.fs.hpp | 16 +++-
far/setattr.cpp | 105 +++++++++++----------
far/sqlite.config.h | 1 +
21 files changed, 374 insertions(+), 218 deletions(-)
diff --git a/far/color_picker.cpp b/far/color_picker.cpp
index 86a2bc016..fb9180e03 100644
--- a/far/color_picker.cpp
+++ b/far/color_picker.cpp
@@ -304,10 +304,6 @@ intptr_t single_color_state::GetSingleColorDlgProc(Dialog* Dlg, intptr_t Msg, in
switch (Msg)
{
- case DN_INITDIALOG:
- Dlg->SendMessage(DM_EDITUNCHANGEDFLAG, Offset + cb::colorcode_edit, {});
- break;
-
case DN_CTLCOLORDLGITEM:
if (in_closed_range(cb::color_first_radio, as_signed(Param1 - Offset), cb::color_last_radio))
{
diff --git a/far/copy.cpp b/far/copy.cpp
index 462442b22..4dd6b6a4c 100644
--- a/far/copy.cpp
+++ b/far/copy.cpp
@@ -244,13 +244,13 @@ static bool set_file_time(const os::fs::file& File, const auto& Times, bool cons
{
const auto opt_time = [&](const os::chrono::time_point& Time)
{
- return All? &Time : nullptr;
+ return All? Time : os::chrono::time_point{};
};
return File.SetTime(
opt_time(Times.CreationTime),
opt_time(Times.LastAccessTime),
- &Times.LastWriteTime,
+ Times.LastWriteTime,
opt_time(Times.ChangeTime)
);
}
@@ -3041,7 +3041,7 @@ bool ShellCopy::AskOverwrite(
const auto FormatLine = [&](const os::chrono::time_point TimePoint, lng Label, unsigned long long Size)
{
- const auto [Date, Time] = time_point_to_string(TimePoint, 8, 1);
+ const auto [Date, Time] = time_point_to_localtime_string(TimePoint, 8, 1);
return far::format(L"{:26} {:20} {} {}"sv, msg(Label), Size, Date, Time);
};
diff --git a/far/datetime.cpp b/far/datetime.cpp
index 4e41efb10..bd2db1196 100644
--- a/far/datetime.cpp
+++ b/far/datetime.cpp
@@ -491,34 +491,25 @@ os::chrono::time parse_time(string_view Date, string_view Time, int DateFormat)
};
}
-os::chrono::time_point ParseTimePoint(string_view const Date, string_view const Time, int const DateFormat)
+os::chrono::time merge_time(os::chrono::time const Default, os::chrono::time const New)
{
- const auto ParsedTime = parse_time(Date, Time, DateFormat);
+ auto Result = New;
- if (ParsedTime.Year == time_none || ParsedTime.Month == time_none || ParsedTime.Day == time_none)
+ const auto inherit = [&](auto const Getter)
{
- // Year / Month / Day can't have reasonable defaults
- return {};
- }
-
- const auto Default = [](auto const Value)
- {
- // Everything else can
- return Value == time_none? 0 : Value;
+ if (auto& Value = std::invoke(Getter, Result); Value == time_none)
+ Value = std::invoke(Getter, Default);
};
- os::chrono::local_time LocalTime{ ParsedTime };
-
- LocalTime.Hours = Default(ParsedTime.Hours);
- LocalTime.Minutes = Default(ParsedTime.Minutes);
- LocalTime.Seconds = Default(ParsedTime.Seconds);
- LocalTime.Hectonanoseconds = Default(ParsedTime.Hectonanoseconds);
+ inherit(&os::chrono::time::Year);
+ inherit(&os::chrono::time::Month);
+ inherit(&os::chrono::time::Day);
+ inherit(&os::chrono::time::Hours);
+ inherit(&os::chrono::time::Minutes);
+ inherit(&os::chrono::time::Seconds);
+ inherit(&os::chrono::time::Hectonanoseconds);
- os::chrono::time_point TimePoint;
- if (!local_to_utc(LocalTime, TimePoint))
- return {};
-
- return TimePoint;
+ return Result;
}
os::chrono::duration ParseDuration(string_view const Date, string_view const Time)
@@ -535,13 +526,8 @@ os::chrono::duration ParseDuration(string_view const Date, string_view const Tim
return days(DateN[0]) + hours(TimeN[0]) + minutes(TimeN[1]) + seconds(TimeN[2]) + os::chrono::hectonanoseconds(TimeN[3]);
}
-std::tuple<string, string> time_point_to_string(os::chrono::time_point const Point, int const TimeLength, int const FullYear, bool const Brief, bool const TextMonth, os::chrono::time_point const CurrentTime)
+static std::tuple<string, string> time_to_string(os::chrono::time Time, int const TimeLength, int const FullYear, bool const Brief, bool const IsRecent, bool const TextMonth)
{
- if (Point == os::chrono::time_point{})
- {
- return {};
- }
-
const auto DateFormat = locale.date_format();
const auto DateSeparator = locale.date_separator();
const auto TimeSeparator = locale.time_separator();
@@ -549,39 +535,35 @@ std::tuple<string, string> time_point_to_string(os::chrono::time_point const Poi
const auto CurDateFormat = Brief && DateFormat == date_type::ymd? date_type::mdy : DateFormat;
- os::chrono::local_time LocalTime;
- if (!utc_to_local(Point, LocalTime))
- return {};
-
auto Letter = L""sv;
if (TimeLength == 6)
{
- Letter = LocalTime.Hours < 12 ? L"a"sv : L"p"sv;
+ Letter = Time.Hours < 12 ? L"a"sv : L"p"sv;
- if (LocalTime.Hours > 12)
- LocalTime.Hours -= 12;
+ if (Time.Hours > 12)
+ Time.Hours -= 12;
- if (!LocalTime.Hours)
- LocalTime.Hours = 12;
+ if (!Time.Hours)
+ Time.Hours = 12;
}
auto TimeText = TimeLength < 7?
- far::format(L"{:02}{}{:02}{}"sv, LocalTime.Hours, TimeSeparator, LocalTime.Minutes, Letter) :
+ far::format(L"{:02}{}{:02}{}"sv, Time.Hours, TimeSeparator, Time.Minutes, Letter) :
cut_right(
far::format(
L"{0:02}{1}{2:02}{1}{3:02}{4}{5:07}"sv,
- LocalTime.Hours,
+ Time.Hours,
TimeSeparator,
- LocalTime.Minutes,
- LocalTime.Seconds,
+ Time.Minutes,
+ Time.Seconds,
DecimalSeparator,
- LocalTime.Hectonanoseconds
+ Time.Hectonanoseconds
),
TimeLength
);
- const auto Year = FullYear? LocalTime.Year : LocalTime.Year % 100;
+ const auto Year = FullYear? Time.Year : Time.Year % 100;
string DateText;
@@ -591,7 +573,7 @@ std::tuple<string, string> time_point_to_string(os::chrono::time_point const Poi
const auto Format = [&](date_format_string const& FormatString)
{
- DateText = far::format(FormatString, LocalTime.Day, locale.LocalNames().Months[LocalTime.Month - 1].Short, Year);
+ DateText = far::format(FormatString, Time.Day, locale.LocalNames().Months[Time.Month - 1].Short, Year);
};
switch (CurDateFormat)
@@ -615,19 +597,19 @@ std::tuple<string, string> time_point_to_string(os::chrono::time_point const Poi
p1 = Year;
w1 = FullYear == 2? 5 : 2;
std::ranges::swap(f1, f3);
- p2 = LocalTime.Month;
- p3 = LocalTime.Day;
+ p2 = Time.Month;
+ p3 = Time.Day;
break;
case date_type::dmy:
- p1 = LocalTime.Day;
- p2 = LocalTime.Month;
+ p1 = Time.Day;
+ p2 = Time.Month;
p3 = Year;
break;
case date_type::mdy:
- p1 = LocalTime.Month;
- p2 = LocalTime.Day;
+ p1 = Time.Month;
+ p2 = Time.Day;
p3 = Year;
break;
}
@@ -643,17 +625,29 @@ std::tuple<string, string> time_point_to_string(os::chrono::time_point const Poi
{
DateText.resize(TextMonth? 6 : 5);
- const auto IsRecent =
- CurrentTime >= Point &&
- CurrentTime - Point < std::chrono::months{ 6 };
-
if (!IsRecent)
- TimeText = far::format(L"{:5}"sv, LocalTime.Year);
+ TimeText = far::format(L"{:5}"sv, Time.Year);
}
return { std::move(DateText), std::move(TimeText) };
}
+static bool is_recent_date(os::chrono::time_point const Point, os::chrono::time_point const CurrentTime)
+{
+ return
+ CurrentTime >= Point &&
+ CurrentTime - Point < std::chrono::months{ 6 };
+}
+
+std::tuple<string, string> time_point_to_localtime_string(os::chrono::time_point const Point, int const TimeLength, int const FullYear, bool const Brief, bool const TextMonth, os::chrono::time_point const CurrentTime)
+{
+ os::chrono::local_time LocalTime;
+ if (!os::chrono::timepoint_to_localtime(Point, LocalTime))
+ return {};
+
+ return time_to_string(LocalTime, TimeLength, FullYear, Brief, is_recent_date(Point, CurrentTime), TextMonth);
+}
+
template<typename T> requires (T::period::num < T::period::den && T::period::num == 1 && T::period::den % 10 == 0)
static constexpr auto decimal_duration_width()
{
@@ -804,7 +798,7 @@ string timestamp(os::chrono::time const Time)
string timestamp(os::chrono::time_point const Point)
{
os::chrono::utc_time UtcTime;
- if (!timepoint_to_utc_time(Point, UtcTime))
+ if (!timepoint_to_utc(Point, UtcTime))
{
LOGWARNING(L"FileTimeToSystemTime(): {}"sv, os::last_error());
return far::format(L"{:16X}"sv, Point.time_since_epoch().count());
@@ -924,6 +918,43 @@ TEST_CASE("datetime.parse.timepoint")
}
}
+TEST_CASE("datetime.merge_time")
+{
+ constexpr auto tn = time_none;
+
+ static constexpr struct
+ {
+ os::chrono::time
+ Time,
+ Result;
+ }
+ Tests[]
+ {
+ { { 2026, 4, 6, 12, 34, 56, 1234567 }, { 2026, 4, 6, 12, 34, 56, 1234567 } },
+ { { tn, 4, 6, 12, 34, 56, 1234567 }, { 1996, 4, 6, 12, 34, 56, 1234567 } },
+ { { tn, tn, 6, 12, 34, 56, 1234567 }, { 1996, 9, 6, 12, 34, 56, 1234567 } },
+ { { tn, tn, tn, 12, 34, 56, 1234567 }, { 1996, 9, 10, 12, 34, 56, 1234567 } },
+ { { tn, tn, tn, tn, 34, 56, 1234567 }, { 1996, 9, 10, 22, 34, 56, 1234567 } },
+ { { tn, tn, tn, tn, tn, 56, 1234567 }, { 1996, 9, 10, 22, 38, 56, 1234567 } },
+ { { tn, tn, tn, tn, tn, tn, 1234567 }, { 1996, 9, 10, 22, 38, 56, 1234567 } },
+ { { tn, tn, tn, tn, tn, tn, tn }, { 1996, 9, 10, 22, 38, 56, 7890123 } },
+ { { 58008, tn, tn, tn, tn, tn, tn }, { 58008, 9, 10, 22, 38, 56, 7890123 } },
+ { { 58008, 13, tn, tn, tn, tn, tn }, { 58008, 13, 10, 22, 38, 56, 7890123 } },
+ { { 58008, 13, 37, tn, tn, tn, tn }, { 58008, 13, 37, 22, 38, 56, 7890123 } },
+ { { 58008, 13, 37, 53, tn, tn, tn }, { 58008, 13, 37, 53, 38, 56, 7890123 } },
+ { { 58008, 13, 37, 53, 17, tn, tn }, { 58008, 13, 37, 53, 17, 56, 7890123 } },
+ { { 58008, 13, 37, 53, 17, 71, tn }, { 58008, 13, 37, 53, 17, 71, 7890123 } },
+ { { 58008, 13, 37, 53, 17, 71, 8771441 }, { 58008, 13, 37, 53, 17, 71, 8771441 } },
+ };
+
+ os::chrono::time constexpr Time{ 1996, 9, 10, 22, 38, 56, 7890123 };
+
+ for (const auto& i: Tests)
+ {
+ REQUIRE(i.Result == merge_time(Time, i.Time));
+ }
+}
+
TEST_CASE("datetime.decimal_duration_width")
{
using namespace std::chrono;
diff --git a/far/datetime.hpp b/far/datetime.hpp
index ce90bb63d..3953cdef3 100644
--- a/far/datetime.hpp
+++ b/far/datetime.hpp
@@ -75,7 +75,7 @@ private:
time_none;
os::chrono::time parse_time(string_view Date, string_view Time, int DateFormat);
-os::chrono::time_point ParseTimePoint(string_view Date, string_view Time, int DateFormat);
+os::chrono::time merge_time(os::chrono::time Default, os::chrono::time New);
os::chrono::duration ParseDuration(string_view Date, string_view Time);
/*
@@ -88,7 +88,7 @@ FullYear:
Windows supports years 1601 through 30827.
*/
// (date, time)
-std::tuple<string, string> time_point_to_string(os::chrono::time_point Point, int TimeLength, int FullYear, bool Brief = false, bool TextMonth = false, os::chrono::time_point CurrentTime = {});
+std::tuple<string, string> time_point_to_localtime_string(os::chrono::time_point Point, int TimeLength, int FullYear, bool Brief = false, bool TextMonth = false, os::chrono::time_point CurrentTime = {});
// (days, time)
std::tuple<string, string> duration_to_string(os::chrono::duration Duration);
diff --git a/far/exception_handler.cpp b/far/exception_handler.cpp
index 1bfab6739..eb7a0dbce 100644
--- a/far/exception_handler.cpp
+++ b/far/exception_handler.cpp
@@ -516,7 +516,7 @@ static string system_timestamp()
static string local_timestamp()
{
os::chrono::local_time LocalTime;
- if (!os::chrono::utc_to_local(os::chrono::nt_clock::now(), LocalTime))
+ if (!os::chrono::timepoint_to_localtime(os::chrono::nt_clock::now(), LocalTime))
return {};
return far::format(L"{} {}"sv, timestamp(LocalTime), MkStrFTime(L"%z, %Z"sv));
diff --git a/far/file_io.cpp b/far/file_io.cpp
index a8c856bf9..09f5a7587 100644
--- a/far/file_io.cpp
+++ b/far/file_io.cpp
@@ -135,7 +135,7 @@ void save_file_with_replace(string_view const FileName, os::fs::attributes const
{
if (os::chrono::time_point CreationTime; os::fs::GetFileTimeSimple(FileName, &CreationTime, {}, {}, {}))
{
- OutFile.SetTime(&CreationTime, {}, {}, {});
+ OutFile.SetTime(CreationTime, {}, {}, {});
}
}
}
diff --git a/far/fileattr.cpp b/far/fileattr.cpp
index acaff175b..24e8823ac 100644
--- a/far/fileattr.cpp
+++ b/far/fileattr.cpp
@@ -134,13 +134,18 @@ void ESetFileEncryption(string_view const Name, bool const State, os::fs::attrib
void ESetFileTime(
string_view const Name,
- os::chrono::time_point const* const LastWriteTime,
- os::chrono::time_point const* const CreationTime,
- os::chrono::time_point const* const LastAccessTime,
- os::chrono::time_point const* const ChangeTime,
+ os::chrono::time_point const LastWriteTime,
+ os::chrono::time_point const CreationTime,
+ os::chrono::time_point const LastAccessTime,
+ os::chrono::time_point const ChangeTime,
bool& SkipErrors)
{
- if (!LastWriteTime && !CreationTime && !LastAccessTime && !ChangeTime)
+ if (constexpr os::chrono::time_point Empty{};
+ LastWriteTime == Empty &&
+ CreationTime == Empty &&
+ LastAccessTime == Empty &&
+ ChangeTime == Empty
+ )
return;
const auto Implementation = [&]
diff --git a/far/fileattr.hpp b/far/fileattr.hpp
index f36bae86c..3fe16a63b 100644
--- a/far/fileattr.hpp
+++ b/far/fileattr.hpp
@@ -52,7 +52,7 @@ void ESetFileAttributes(string_view Name, os::fs::attributes Attributes, bool& S
void ESetFileCompression(string_view Name, bool State, os::fs::attributes CurrentAttributes, bool& SkipErrors);
void ESetFileEncryption(string_view Name, bool State, os::fs::attributes CurrentAttributes, bool& SkipErrors);
void ESetFileSparse(string_view Name, bool State, os::fs::attributes CurrentAttributes, bool& SkipErrors);
-void ESetFileTime(string_view Name, const os::chrono::time_point* LastWriteTime, const os::chrono::time_point* CreationTime, const os::chrono::time_point* LastAccessTime, const os::chrono::time_point* ChangeTime, bool& SkipErrors);
+void ESetFileTime(string_view Name, os::chrono::time_point LastWriteTime, os::chrono::time_point CreationTime, os::chrono::time_point LastAccessTime, os::chrono::time_point ChangeTime, bool& SkipErrors);
void ESetFileOwner(const string& Computer, string_view Name, const string& Owner, bool& SkipErrors);
void EDeleteReparsePoint(string_view Name, os::fs::attributes CurrentAttributes, bool& SkipErrors);
diff --git a/far/filefilterparams.cpp b/far/filefilterparams.cpp
index 1125db8f5..e2c8190cf 100644
--- a/far/filefilterparams.cpp
+++ b/far/filefilterparams.cpp
@@ -658,7 +658,7 @@ static intptr_t FileFilterConfigDlgProc(Dialog* Dlg,intptr_t Msg,intptr_t Param1
else if (Param1==ID_FF_CURRENT || Param1==ID_FF_BLANK)
{
const auto& [Date, Time] = Param1==ID_FF_CURRENT?
- time_point_to_string(os::chrono::nt_clock::now(), 16, 2) :
+ time_point_to_localtime_string(os::chrono::nt_clock::now(), 16, 2) :
std::tuple<string, string>{};
SCOPED_ACTION(Dialog::suppress_redraw)(Dlg);
@@ -668,14 +668,10 @@ static intptr_t FileFilterConfigDlgProc(Dialog* Dlg,intptr_t Msg,intptr_t Param1
const auto da = relative? ID_FF_DAYSAFTEREDIT : ID_FF_DATEAFTEREDIT;
Dlg->SendMessage(DM_SETTEXTPTR,da, UNSAFE_CSTR(Date));
- Dlg->SendMessage(DM_EDITUNCHANGEDFLAG, da, nullptr);
Dlg->SendMessage(DM_SETTEXTPTR,ID_FF_TIMEAFTEREDIT, UNSAFE_CSTR(Time));
- Dlg->SendMessage(DM_EDITUNCHANGEDFLAG, ID_FF_TIMEAFTEREDIT, nullptr);
Dlg->SendMessage(DM_SETTEXTPTR,db, UNSAFE_CSTR(Date));
- Dlg->SendMessage(DM_EDITUNCHANGEDFLAG, db, nullptr);
Dlg->SendMessage(DM_SETTEXTPTR,ID_FF_TIMEBEFOREEDIT, UNSAFE_CSTR(Time));
- Dlg->SendMessage(DM_EDITUNCHANGEDFLAG, ID_FF_TIMEBEFOREEDIT, nullptr);
Dlg->SendMessage(DM_SETFOCUS, db, nullptr);
@@ -1017,7 +1013,7 @@ bool FileFilterConfig(FileFilterParams& Filter, bool ColorConfig)
const auto ProcessPoint = [&](auto Point, auto DateId, auto TimeId)
{
FilterDlg[ID_FF_DATERELATIVE].Selected = BSTATE_UNCHECKED;
- std::tie(FilterDlg[DateId].strData, FilterDlg[TimeId].strData) = time_point_to_string(Point, 16, 2);
+ std::tie(FilterDlg[DateId].strData, FilterDlg[TimeId].strData) = time_point_to_localtime_string(Point, 16, 2);
};
Dates.visit(overload
@@ -1129,6 +1125,15 @@ bool FileFilterConfig(FileFilterParams& Filter, bool ColorConfig)
FilterDlg[ID_FF_TIMEBEFOREEDIT].strData[8] = TimeSeparator;
FilterDlg[ID_FF_TIMEAFTEREDIT].strData[8] = TimeSeparator;
+ const auto ParseTimePoint = [&](int const DateId, int const TimeId)
+ {
+ const auto MergedTime = merge_time({}, parse_time(FilterDlg[DateId].strData, FilterDlg[TimeId].strData, static_cast<int>(DateFormat)));
+
+ os::chrono::time_point TimePoint;
+ (void)os::chrono::localtime_to_timepoint(os::chrono::local_time{ MergedTime }, TimePoint);
+ return TimePoint;
+ };
+
const auto NewDates = IsRelative?
filter_dates
(
@@ -1137,8 +1142,8 @@ bool FileFilterConfig(FileFilterParams& Filter, bool ColorConfig)
) :
filter_dates
(
- ParseTimePoint(FilterDlg[ID_FF_DATEAFTEREDIT].strData, FilterDlg[ID_FF_TIMEAFTEREDIT].strData, static_cast<int>(DateFormat)),
- ParseTimePoint(FilterDlg[ID_FF_DATEBEFOREEDIT].strData, FilterDlg[ID_FF_TIMEBEFOREEDIT].strData, static_cast<int>(DateFormat))
+ ParseTimePoint(ID_FF_DATEAFTEREDIT, ID_FF_TIMEAFTEREDIT),
+ ParseTimePoint(ID_FF_DATEBEFOREEDIT, ID_FF_TIMEBEFOREEDIT)
);
Filter.SetDate(FilterDlg[ID_FF_MATCHDATE].Selected != 0, static_cast<enumFDateType>(FilterDlg[ID_FF_DATETYPE].ListPos), NewDates);
diff --git a/far/history.cpp b/far/history.cpp
index 3069f93a3..d50487836 100644
--- a/far/history.cpp
+++ b/far/history.cpp
@@ -292,14 +292,14 @@ history_return_type History::ProcessMenu(string& strStr, UUID* const Uuid, strin
}
}
- if (os::chrono::local_time SavedTime; utc_to_local(i.Time, SavedTime) && (LastDay != SavedTime.Day || LastMonth != SavedTime.Month || LastYear != SavedTime.Year))
+ if (os::chrono::local_time SavedTime; os::chrono::timepoint_to_localtime(i.Time, SavedTime) && (LastDay != SavedTime.Day || LastMonth != SavedTime.Month || LastYear != SavedTime.Year))
{
LastDay = SavedTime.Day;
LastMonth = SavedTime.Month;
LastYear = SavedTime.Year;
menu_item_ex Separator{ LIF_SEPARATOR };
string Time;
- std::tie(Separator.Name, Time) = time_point_to_string(i.Time, 8, 1);
+ std::tie(Separator.Name, Time) = time_point_to_localtime_string(i.Time, 8, 1);
HistoryMenu.AddItem(Separator);
}
strRecord += i.Name;
diff --git a/far/imports.hpp b/far/imports.hpp
index 0f124d53f..86d1c686d 100644
--- a/far/imports.hpp
+++ b/far/imports.hpp
@@ -163,6 +163,8 @@ public: \
DEFINE_IMPORT_FUNCTION(kernel32, le, false, WINAPI, BOOL, GetPhysicallyInstalledSystemMemory, PULONGLONG TotalMemoryInKilobytes); // Vista SP1
DEFINE_IMPORT_FUNCTION(kernel32, le, false, WINAPI, BOOLEAN, TryAcquireSRWLockExclusive, PSRWLOCK SRWLock); // 7
DEFINE_IMPORT_FUNCTION(kernel32, le, false, WINAPI, BOOLEAN, TryAcquireSRWLockShared, PSRWLOCK SRWLock); // 7
+ DEFINE_IMPORT_FUNCTION(kernel32, le, false, WINAPI, BOOL, SystemTimeToTzSpecificLocalTimeEx, const DYNAMIC_TIME_ZONE_INFORMATION* TimeZoneInformation, const SYSTEMTIME* UniversalTime, LPSYSTEMTIME LocalTime); // 7
+ DEFINE_IMPORT_FUNCTION(kernel32, le, false, WINAPI, BOOL, TzSpecificLocalTimeToSystemTimeEx, const DYNAMIC_TIME_ZONE_INFORMATION* TimeZoneInformation, const SYSTEMTIME* LocalTime, LPSYSTEMTIME UniversalTime); // 7
DEFINE_IMPORT_FUNCTION(kernel32, le, void, WINAPI, void, GetSystemTimePreciseAsFileTime, LPFILETIME SystemTimeAsFileTime); // 8
DEFINE_IMPORT_FUNCTION(kernel32, le, hr, WINAPI, HRESULT, SetThreadDescription, HANDLE Thread, PCWSTR ThreadDescription); // 10
DEFINE_IMPORT_FUNCTION(kernel32, le, hr, WINAPI, HRESULT, GetThreadDescription, HANDLE Thread, PWSTR* ThreadDescription); // 10
diff --git a/far/interf.cpp b/far/interf.cpp
index f3ee65023..1d7258344 100644
--- a/far/interf.cpp
+++ b/far/interf.cpp
@@ -903,7 +903,7 @@ void chars_to_cells(string_view Str, size_t& CharsConsumed, size_t const CellsAv
assert(CharsConsumed < Str.size() || CellsConsumed == visual_string_length(Str));
}
-std::vector<FAR_CHAR_INFO> text_to_char_info(string_view Str, size_t& CharsConsumed, size_t const CellsAvailable, size_t& CellsConsumed)
+static std::vector<FAR_CHAR_INFO> text_to_char_info(string_view Str, size_t& CharsConsumed, size_t const CellsAvailable, size_t& CellsConsumed)
{
CharsConsumed = 0;
CellsConsumed = 0;
diff --git a/far/locale.cpp b/far/locale.cpp
index 595d3bef7..c46c03cdf 100644
--- a/far/locale.cpp
+++ b/far/locale.cpp
@@ -289,7 +289,7 @@ namespace detail
if (m_IsInvariant)
{
m_IsCJK = false;
- m_DateFormat = date_type::mdy;
+ m_DateFormat = date_type::ymd;
m_DigitsGrouping = 3;
m_DateSeparator = L'/';
m_TimeSeparator = L':';
diff --git a/far/macroapi.cpp b/far/macroapi.cpp
index ce732d4eb..3105ce291 100644
--- a/far/macroapi.cpp
+++ b/far/macroapi.cpp
@@ -3230,7 +3230,7 @@ void FarMacroApi::panelitemFunc() const
const auto FormatDate = [](const os::chrono::time_point TimePoint)
{
- const auto& [Date, Time] = time_point_to_string(TimePoint, 8, 1);
+ const auto& [Date, Time] = time_point_to_localtime_string(TimePoint, 8, 1);
return concat(Date, L' ', Time);
};
diff --git a/far/panelmix.cpp b/far/panelmix.cpp
index d95e5a2ce..94459824e 100644
--- a/far/panelmix.cpp
+++ b/far/panelmix.cpp
@@ -653,7 +653,7 @@ string FormatStr_DateTime(os::chrono::time_point FileTime, column_type const Col
break;
}
- const auto& [Date, Time] = time_point_to_string(FileTime, ColumnWidth, FullYear, Brief, TextMonth, CurrentTime);
+ const auto& [Date, Time] = time_point_to_localtime_string(FileTime, ColumnWidth, FullYear, Brief, TextMonth, CurrentTime);
string OutStr;
diff --git a/far/platform.chrono.cpp b/far/platform.chrono.cpp
index 329f6af47..0acfa3929 100644
--- a/far/platform.chrono.cpp
+++ b/far/platform.chrono.cpp
@@ -37,6 +37,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Internal:
#include "imports.hpp"
+#include "log.hpp"
// Platform:
#include "platform.hpp"
@@ -47,6 +48,23 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//----------------------------------------------------------------------------
+template<>
+struct formattable<SYSTEMTIME>
+{
+ static string to_string(SYSTEMTIME const& Time)
+ {
+ return far::format(L"{{{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:03}}}"sv,
+ Time.wYear,
+ Time.wMonth,
+ Time.wDay,
+ Time.wHour,
+ Time.wMinute,
+ Time.wSecond,
+ Time.wMilliseconds
+ );
+ }
+};
+
namespace os::chrono
{
nt_clock::time_point nt_clock::now() noexcept
@@ -139,7 +157,7 @@ namespace os::chrono
utc_time now_utc()
{
// hns precision
- if (utc_time UtcTime; timepoint_to_utc_time(nt_clock::now(), UtcTime))
+ if (utc_time UtcTime; timepoint_to_utc(nt_clock::now(), UtcTime))
return UtcTime;
// ms precision
@@ -151,7 +169,7 @@ namespace os::chrono
local_time now_local()
{
// hns precision
- if (local_time LocalTime; utc_to_local(nt_clock::now(), LocalTime))
+ if (local_time LocalTime; timepoint_to_localtime(nt_clock::now(), LocalTime))
return LocalTime;
// ms precision
@@ -166,13 +184,26 @@ namespace os::chrono
return FileTimeToSystemTime(&FileTime, &SystemTime);
}
+ static bool system_time_to_timepoint(SYSTEMTIME const& SystemTime, unsigned const Hectonanoseconds, time_point& TimePoint)
+ {
+ FILETIME FileTime;
+ if (!SystemTimeToFileTime(&SystemTime, &FileTime))
+ {
+ LOGWARNING(L"SystemTimeToFileTime({}): {}"sv, SystemTime, os::last_error());
+ return false;
+ }
+
+ TimePoint = nt_clock::from_filetime(FileTime) + hectonanoseconds{ Hectonanoseconds } % 1ms;
+ return true;
+ }
+
static void transfer_hns(time_point const TimePoint, time& Time)
{
assert(!(Time.Hectonanoseconds % (1ms / 1_hns)));
Time.Hectonanoseconds += TimePoint.time_since_epoch() % 1ms / 1_hns;
}
- bool timepoint_to_utc_time(time_point const TimePoint, utc_time& UtcTime)
+ bool timepoint_to_utc(time_point const TimePoint, utc_time& UtcTime)
{
SYSTEMTIME SystemTime;
if (!timepoint_to_system_time(TimePoint, SystemTime))
@@ -185,89 +216,75 @@ namespace os::chrono
return true;
}
- static bool utc_to_local_impl(SYSTEMTIME const& UtcTime, SYSTEMTIME& LocalTime)
+ bool utc_to_timepoint(utc_time const UtcTime, time_point& TimePoint)
{
- return SystemTimeToTzSpecificLocalTime({}, &UtcTime, &LocalTime) != FALSE;
+ return system_time_to_timepoint(make_system_time(UtcTime), UtcTime.Hectonanoseconds, TimePoint);
}
- bool utc_to_local(time_point UtcTime, local_time& LocalTime)
+ static SYSTEMTIME utc_to_local_impl(SYSTEMTIME const& UtcTime)
+ {
+ SYSTEMTIME LocalTime;
+
+ if (imports.SystemTimeToTzSpecificLocalTimeEx && imports.SystemTimeToTzSpecificLocalTimeEx({}, &UtcTime, &LocalTime))
+ return LocalTime;
+
+ if (SystemTimeToTzSpecificLocalTime({}, &UtcTime, &LocalTime))
+ return LocalTime;
+
+ if (FILETIME UtcFileTime, LocalFileTime;
+ SystemTimeToFileTime(&UtcTime, &UtcFileTime) &&
+ FileTimeToLocalFileTime(&UtcFileTime, &LocalFileTime) &&
+ FileTimeToSystemTime(&LocalFileTime, &LocalTime)
+ )
+ return LocalTime;
+
+ LOGWARNING(L"Failed to convert UTC {} to local time: {}"sv, UtcTime, os::last_error());
+
+ // Better than nothing
+ return UtcTime;
+ }
+
+ bool timepoint_to_localtime(time_point const TimePoint, local_time& LocalTime)
{
SYSTEMTIME SystemTime;
- if (!timepoint_to_system_time(UtcTime, SystemTime))
+ if (!timepoint_to_system_time(TimePoint, SystemTime))
return false;
- SYSTEMTIME LocalSystemTime;
- if (!utc_to_local_impl(SystemTime, LocalSystemTime))
- return false;
+ const auto LocalSystemTime = utc_to_local_impl(SystemTime);
LocalTime = local_time{ make_time(LocalSystemTime) };
- transfer_hns(UtcTime, LocalTime);
+ transfer_hns(TimePoint, LocalTime);
return true;
}
- static bool local_to_utc_impl(SYSTEMTIME const& LocalTime, SYSTEMTIME& UtcTime)
+ static SYSTEMTIME local_to_utc_impl(SYSTEMTIME const& LocalTime)
{
- if (imports.TzSpecificLocalTimeToSystemTime && imports.TzSpecificLocalTimeToSystemTime(nullptr, &LocalTime, &UtcTime))
- return true;
+ SYSTEMTIME UtcTime;
- TIME_ZONE_INFORMATION Tz;
- if (GetTimeZoneInformation(&Tz) != TIME_ZONE_ID_INVALID)
- {
- Tz.Bias = -Tz.Bias;
- Tz.StandardBias = -Tz.StandardBias;
- Tz.DaylightBias = -Tz.DaylightBias;
- if (SystemTimeToTzSpecificLocalTime(&Tz, &LocalTime, &UtcTime))
- return true;
- }
+ if (imports.TzSpecificLocalTimeToSystemTimeEx && imports.TzSpecificLocalTimeToSystemTimeEx({}, &LocalTime, &UtcTime))
+ return UtcTime;
- std::tm ltm
- {
- LocalTime.wSecond,
- LocalTime.wMinute,
- LocalTime.wHour,
- LocalTime.wDay,
- LocalTime.wMonth - 1,
- LocalTime.wYear - 1900,
- LocalTime.wDayOfWeek,
- -1,
- -1
- };
+ if (imports.TzSpecificLocalTimeToSystemTime && imports.TzSpecificLocalTimeToSystemTime({}, &LocalTime, &UtcTime))
+ return UtcTime;
- if (const auto gtim = std::mktime(<m); gtim != static_cast<time_t>(-1))
- {
- if (const auto ptm = std::gmtime(>im))
- {
- UtcTime.wYear = ptm->tm_year + 1900;
- UtcTime.wMonth = ptm->tm_mon + 1;
- UtcTime.wDay = ptm->tm_mday;
- UtcTime.wHour = ptm->tm_hour;
- UtcTime.wMinute = ptm->tm_min;
- UtcTime.wSecond = ptm->tm_sec;
- UtcTime.wDayOfWeek = ptm->tm_wday;
- UtcTime.wMilliseconds = LocalTime.wMilliseconds;
- return true;
- }
- }
+ if (FILETIME LocalFileTime, UtcFileTime;
+ SystemTimeToFileTime(&LocalTime, &LocalFileTime) &&
+ LocalFileTimeToFileTime(&LocalFileTime, &UtcFileTime) &&
+ FileTimeToSystemTime(&UtcFileTime, &UtcTime)
+ )
+ return UtcTime;
- FILETIME LocalFileTime, UtcFileTime;
- return SystemTimeToFileTime(&LocalTime, &LocalFileTime) && LocalFileTimeToFileTime(&LocalFileTime, &UtcFileTime) && FileTimeToSystemTime(&UtcFileTime, &UtcTime);
+ LOGWARNING(L"Failed to convert local time {} to UTC: {}"sv, LocalTime, os::last_error());
+
+ // Better than nothing
+ return LocalTime;
}
- bool local_to_utc(local_time const LocalTime, time_point& UtcTime)
+ bool localtime_to_timepoint(local_time const LocalTime, time_point& TimePoint)
{
- SYSTEMTIME SystemUtcTime;
- if (!local_to_utc_impl(make_system_time(LocalTime), SystemUtcTime))
- return false;
-
- FILETIME FileUtcTime;
- if (!SystemTimeToFileTime(&SystemUtcTime, &FileUtcTime))
- return false;
-
- UtcTime = nt_clock::from_filetime(FileUtcTime);
- UtcTime += hectonanoseconds{ LocalTime.Hectonanoseconds } % 1ms;
- return true;
+ return system_time_to_timepoint(local_to_utc_impl(make_system_time(LocalTime)), LocalTime.Hectonanoseconds, TimePoint);
}
void sleep_for(std::chrono::milliseconds const Duration)
@@ -288,7 +305,7 @@ namespace os::chrono
string wall_time(time_point const Time)
{
local_time LocalTime;
- if (!utc_to_local(Time, LocalTime))
+ if (!timepoint_to_localtime(Time, LocalTime))
{
return {};
}
@@ -304,3 +321,83 @@ namespace os::chrono
return Value;
}
}
+
+#ifdef ENABLE_TESTS
+
+#include "testing.hpp"
+
+TEST_CASE("chrono.timepoint_conversions")
+{
+ enum direction
+ {
+ to_time = 0_bit,
+ from_time = 1_bit,
+ both = to_time | from_time,
+ };
+
+ static constexpr struct
+ {
+ std::optional<os::chrono::time> Time;
+ std::optional<os::chrono::duration> TimeSinceEpoch;
+ direction Direction = direction::both;
+ }
+ Tests[]
+ {
+ { {{ 1600, 1, 1, 0, 0, 0, 0 }}, { } }, // Prehistoric
+ { {{ 1601, 1, 1, 0, 0, 0, 0 }}, { 0_hns, } }, // Epoch
+ { {{ 1601, 1, 1, 0, 0, 0, 1 }}, { 1_hns, } },
+ { {{ 2026, 4, 6, 13, 47, 4, 1234567 }}, { 134199568241234567_hns }, },
+ { {{ 30827, 12, 31, 23, 59, 59, 9990000 }}, { 0x7fff35f4f06c58f0_hns }, }, // Latest possible SYSTEMTIME
+ { {{ 30828, 9, 14, 2, 48, 5, 4775807 }}, { 0x7fffffffffffffff_hns }, to_time }, // Latest possible FILETIME
+ { {{ 30828, 9, 14, 2, 48, 5, 4775808 }}, { }, }, // Post-apocalyptic
+ { {{ 65535, 13, 32, 25, 60, 60, 9999999 }}, { }, }, // Rubbish
+ };
+
+ for (const auto& i: Tests)
+ {
+ if (i.Time)
+ {
+ if (i.TimeSinceEpoch)
+ {
+ if (i.Direction & direction::from_time)
+ {
+ os::chrono::time_point UtcResult;
+ REQUIRE(os::chrono::utc_to_timepoint(os::chrono::utc_time{ *i.Time }, UtcResult));
+ REQUIRE(i.TimeSinceEpoch == UtcResult.time_since_epoch());
+ }
+
+ if (i.Direction & direction::to_time)
+ {
+ os::chrono::utc_time UtcTimeResult;
+ REQUIRE(os::chrono::timepoint_to_utc(os::chrono::time_point{ *i.TimeSinceEpoch }, UtcTimeResult));
+ REQUIRE(*i.Time == UtcTimeResult);
+ }
+
+ if (i.Direction == direction::both)
+ {
+ os::chrono::local_time LocalTimeResult;
+ REQUIRE(os::chrono::timepoint_to_localtime(os::chrono::time_point{ *i.TimeSinceEpoch }, LocalTimeResult));
+ os::chrono::time_point TimePoint;
+ REQUIRE(os::chrono::localtime_to_timepoint(LocalTimeResult, TimePoint));
+ REQUIRE(i.TimeSinceEpoch == TimePoint.time_since_epoch());
+ }
+ }
+ else
+ {
+ os::chrono::time_point UtcResult;
+ REQUIRE(!os::chrono::utc_to_timepoint(os::chrono::utc_time{ *i.Time }, UtcResult));
+ }
+ }
+ else
+ {
+ if (i.TimeSinceEpoch)
+ {
+ os::chrono::utc_time UtcTimeResult;
+ REQUIRE(!os::chrono::timepoint_to_utc(os::chrono::time_point{ *i.TimeSinceEpoch }, UtcTimeResult));
+ }
+ else
+ assert(false);
+ }
+ }
+}
+#endif
diff --git a/far/platform.chrono.hpp b/far/platform.chrono.hpp
index fddf2e5ed..7e8a0c626 100644
--- a/far/platform.chrono.hpp
+++ b/far/platform.chrono.hpp
@@ -127,10 +127,11 @@ namespace os::chrono
utc_time now_utc();
local_time now_local();
- bool timepoint_to_utc_time(time_point TimePoint, utc_time& UtcTime);
+ bool timepoint_to_utc(time_point TimePoint, utc_time& UtcTime);
+ bool utc_to_timepoint(utc_time UtcTime, time_point& TimePoint);
- bool utc_to_local(time_point UtcTime, local_time& LocalTime);
- bool local_to_utc(local_time LocalTime, time_point& UtcTime);
+ bool timepoint_to_localtime(time_point TimePoint, local_time& LocalTime);
+ bool localtime_to_timepoint(local_time LocalTime, time_point& TimePoint);
// Q: WTF is this, it's in the standard!
// A: MSVC implemented it in terms of sleep_until, which is mental
diff --git a/far/platform.fs.cpp b/far/platform.fs.cpp
index 0d31e3885..ff2adab35 100644
--- a/far/platform.fs.cpp
+++ b/far/platform.fs.cpp
@@ -909,12 +909,17 @@ namespace os::fs
return false;
}
- bool file::GetTime(os::chrono::time_point* CreationTime, os::chrono::time_point* LastAccessTime, os::chrono::time_point* LastWriteTime, os::chrono::time_point* ChangeTime) const
+ bool file::GetTime(
+ chrono::time_point* const CreationTime,
+ chrono::time_point* const LastAccessTime,
+ chrono::time_point* const LastWriteTime,
+ chrono::time_point* const ChangeTime
+ ) const
{
- const auto convert_time = [](LARGE_INTEGER const& From, os::chrono::time_point* const To)
+ const auto convert_time = [](LARGE_INTEGER const& From, chrono::time_point* const To)
{
if (To)
- *To = os::chrono::nt_clock::from_hectonanoseconds(From.QuadPart);
+ *To = chrono::nt_clock::from_hectonanoseconds(From.QuadPart);
};
FILE_BASIC_INFORMATION fbi;
@@ -930,12 +935,16 @@ namespace os::fs
return true;
}
- bool file::SetTime(const os::chrono::time_point* CreationTime, const os::chrono::time_point* LastAccessTime, const os::chrono::time_point* LastWriteTime, const os::chrono::time_point* ChangeTime) const
+ bool file::SetTime(
+ chrono::time_point const CreationTime,
+ chrono::time_point const LastAccessTime,
+ chrono::time_point const LastWriteTime,
+ chrono::time_point const ChangeTime
+ ) const
{
- const auto convert_time = [](os::chrono::time_point const* const From, LARGE_INTEGER& To)
+ const auto convert_time = [](chrono::time_point const From, LARGE_INTEGER& To)
{
- if (From)
- To.QuadPart = os::chrono::nt_clock::to_hectonanoseconds(*From);
+ To.QuadPart = chrono::nt_clock::to_hectonanoseconds(From);
};
FILE_BASIC_INFORMATION fbi{};
diff --git a/far/platform.fs.hpp b/far/platform.fs.hpp
index 57457c09e..4bc775ec6 100644
--- a/far/platform.fs.hpp
+++ b/far/platform.fs.hpp
@@ -263,9 +263,19 @@ namespace os::fs
bool SetEnd();
[[nodiscard]]
- bool GetTime(chrono::time_point* CreationTime, chrono::time_point* LastAccessTime, chrono::time_point* LastWriteTime, chrono::time_point* ChangeTime) const;
-
- bool SetTime(const chrono::time_point* CreationTime, const chrono::time_point* LastAccessTime, const chrono::time_point* LastWriteTime, const chrono::time_point* ChangeTime) const;
+ bool GetTime(
+ chrono::time_point* CreationTime,
+ chrono::time_point* LastAccessTime,
+ chrono::time_point* LastWriteTime,
+ chrono::time_point* ChangeTime
+ ) const;
+
+ bool SetTime(
+ chrono::time_point CreationTime,
+ chrono::time_point LastAccessTime,
+ chrono::time_point LastWriteTime,
+ chrono::time_point ChangeTime
+ ) const;
[[nodiscard]]
bool GetSize(unsigned long long& Size) const;
diff --git a/far/setattr.cpp b/far/setattr.cpp
index d703a47ce..fd7f7cdbb 100644
--- a/far/setattr.cpp
+++ b/far/setattr.cpp
@@ -270,28 +270,57 @@ struct SetAttrDlgParam
Owner;
};
-static auto time_point_to_string(os::chrono::time_point const TimePoint)
+static auto setattr_time_point_to_localtime_string(os::chrono::time_point const TimePoint)
{
- return time_point_to_string(TimePoint, 16, 2);
+ return time_point_to_localtime_string(TimePoint, 16, 2);
+}
+
+static os::chrono::time construct_time(
+ os::chrono::time const OriginalDateTime,
+ string_view const Date,
+ string_view const Time)
+{
+ return merge_time(OriginalDateTime, parse_time(Date, Time, static_cast<int>(locale.date_format())));
+}
+
+static std::optional<os::chrono::time_point> construct_time_from_localtime(
+ os::chrono::time_point const OriginalTimePoint,
+ string_view const Date,
+ string_view const Time)
+{
+ os::chrono::local_time OriginalDateTime;
+ // OriginalDateTime is only needed for inheriting, i.e. when the user leaves some fields empty.
+ // If we can't obtain it for some reason, e.g. the timestamp is invalid, just use 0.
+ // This will allow to set the timestamp to something reasonable at least.
+ if (!timepoint_to_localtime(OriginalTimePoint, OriginalDateTime))
+ OriginalDateTime = {};
+
+ const auto DateTime = os::chrono::local_time{ construct_time(OriginalDateTime, Date, Time) };
+
+ if (os::chrono::time_point Result; os::chrono::localtime_to_timepoint(DateTime, Result))
+ return Result;
+
+ return {};
}
static void set_date_or_time(Dialog* const Dlg, int const Id, string const& Value, bool const MakeUnchanged)
{
Dlg->SendMessage(DM_SETTEXTPTR, Id, UNSAFE_CSTR(Value));
- Dlg->SendMessage(DM_EDITUNCHANGEDFLAG, Id, ToPtr(MakeUnchanged));
+ if (MakeUnchanged)
+ Dlg->SendMessage(DM_EDITUNCHANGEDFLAG, Id, ToPtr(MakeUnchanged));
}
-static void set_dates_and_times(Dialog* const Dlg, const time_map& TimeMapEntry, std::optional<os::chrono::time_point> const TimePoint, bool const MakeUnchanged)
+static void set_dates_and_times(Dialog* const Dlg, const time_map& TimeMapEntry, std::optional<os::chrono::time_point> const TimePoint)
{
string Date, Time;
if (TimePoint)
{
- std::tie(Date, Time) = time_point_to_string(*TimePoint);
+ std::tie(Date, Time) = setattr_time_point_to_localtime_string(*TimePoint);
}
- set_date_or_time(Dlg, TimeMapEntry.DateId, Date, MakeUnchanged);
- set_date_or_time(Dlg, TimeMapEntry.TimeId, Time, MakeUnchanged);
+ set_date_or_time(Dlg, TimeMapEntry.DateId, Date, false);
+ set_date_or_time(Dlg, TimeMapEntry.TimeId, Time, false);
}
static void AdvancedAttributesDialog(SetAttrDlgParam& DlgParam)
@@ -455,7 +484,7 @@ static intptr_t SetAttrDlgProc(Dialog* Dlg,intptr_t Msg,intptr_t Param1,void* Pa
for (const auto& i: TimeMap)
{
- set_dates_and_times(Dlg, i, Time, false);
+ set_dates_and_times(Dlg, i, Time);
}
Dlg->SendMessage(DM_SETFOCUS, SA_EDIT_WDATE, nullptr);
@@ -476,7 +505,7 @@ static intptr_t SetAttrDlgProc(Dialog* Dlg,intptr_t Msg,intptr_t Param1,void* Pa
SCOPED_ACTION(Dialog::suppress_redraw)(Dlg);
if (Record.Event.MouseEvent.dwEventFlags == DOUBLE_CLICK)
- set_dates_and_times(Dlg, TimeMap[label_to_time_map_index(Param1)], os::chrono::nt_clock::now(), false);
+ set_dates_and_times(Dlg, TimeMap[label_to_time_map_index(Param1)], os::chrono::nt_clock::now());
else
Dlg->SendMessage(DM_SETFOCUS, Param1 + 1, nullptr);
}
@@ -586,38 +615,6 @@ public:
}
};
-static bool construct_time(
- os::chrono::time_point const OriginalFileTime,
- os::chrono::time_point& FileTime,
- string_view const OSrcDate,
- string_view const OSrcTime)
-{
- os::chrono::local_time OriginalLocalTime;
- // OriginalLocalTime is only needed for inheriting, i.e. when the user leaves some fields empty.
- // If we can't obtain it for some reason, e.g. the timestamp is invalid, just use 0.
- // This will allow to set the timestamp to something reasonable at least.
- if (!utc_to_local(OriginalFileTime, OriginalLocalTime))
- OriginalLocalTime = {};
-
- os::chrono::local_time LocalTime{ parse_time(OSrcDate, OSrcTime, static_cast<int>(locale.date_format())) };
-
- const auto inherit = [&](auto Getter)
- {
- if (auto& Value = std::invoke(Getter, LocalTime); Value == time_none)
- Value = std::invoke(Getter, OriginalLocalTime);
- };
-
- inherit(&os::chrono::local_time::Year);
- inherit(&os::chrono::local_time::Month);
- inherit(&os::chrono::local_time::Day);
- inherit(&os::chrono::local_time::Hours);
- inherit(&os::chrono::local_time::Minutes);
- inherit(&os::chrono::local_time::Seconds);
- inherit(&os::chrono::local_time::Hectonanoseconds);
-
- return local_to_utc(LocalTime, FileTime);
-}
-
struct state
{
string const& Owner;
@@ -663,20 +660,22 @@ static bool process_single_file(
}
{
- os::chrono::time_point WriteTime, CreationTime, AccessTime, ChangeTime;
- std::array TimePointers{ &WriteTime, &CreationTime, &AccessTime, &ChangeTime };
-
- for (const auto& [i, TimePointer] : zip(TimeMap, TimePointers))
+ os::chrono::time_point Times[4]{};
+ for (const auto& [i, Time]: zip(TimeMap, Times))
{
const auto OriginalTime = std::invoke(i.Accessor, Current.FindData);
- if (!construct_time(OriginalTime, *TimePointer, DateTimeAccessor(i.DateId), DateTimeAccessor(i.TimeId))
- || *TimePointer == OriginalTime)
- {
- TimePointer = {};
- }
+ if (const auto Result = construct_time_from_localtime(OriginalTime, DateTimeAccessor(i.DateId), DateTimeAccessor(i.TimeId)); Result && *Result != OriginalTime)
+ Time = *Result;
}
- ESetFileTime(Name, TimePointers[0], TimePointers[1], TimePointers[2], TimePointers[3], SkipErrors);
+ ESetFileTime(
+ Name,
+ Times[label_to_time_map_index(SA_TEXT_LASTWRITE)],
+ Times[label_to_time_map_index(SA_TEXT_CREATION)],
+ Times[label_to_time_map_index(SA_TEXT_LASTACCESS)],
+ Times[label_to_time_map_index(SA_TEXT_CHANGE)],
+ SkipErrors
+ );
}
return true;
@@ -946,7 +945,7 @@ static bool ShellSetFileAttributesImpl(Panel* SrcPanel, const string* Object)
for (const auto& [i, State]: zip(TimeMap, DlgParam.Times))
{
- std::tie(State.Date.InitialValue, State.Time.InitialValue) = time_point_to_string(std::invoke(i.Accessor, SingleSelFindData));
+ std::tie(State.Date.InitialValue, State.Time.InitialValue) = setattr_time_point_to_localtime_string(std::invoke(i.Accessor, SingleSelFindData));
AttrDlg[i.DateId].strData = State.Date.InitialValue;
AttrDlg[i.TimeId].strData = State.Time.InitialValue;
@@ -1270,7 +1269,7 @@ static bool ShellSetFileAttributesImpl(Panel* SrcPanel, const string* Object)
if (!Time)
continue;
- std::tie(State.Date.InitialValue, State.Time.InitialValue) = time_point_to_string(*Time);
+ std::tie(State.Date.InitialValue, State.Time.InitialValue) = setattr_time_point_to_localtime_string(*Time);
AttrDlg[i.DateId].strData = State.Date.InitialValue;
AttrDlg[i.TimeId].strData = State.Time.InitialValue;
diff --git a/far/sqlite.config.h b/far/sqlite.config.h
index 4afc1e605..136ef5cde 100644
--- a/far/sqlite.config.h
+++ b/far/sqlite.config.h
@@ -36,6 +36,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define SQLITE_DEFAULT_MEMSTATUS 0
#define SQLITE_DEFAULT_WAL_SYNCHRONOUS 1
+#define SQLITE_LIKE_DOESNT_MATCH_BLOBS
//#define SQLITE_OMIT_AUTHORIZATION 1
//#define SQLITE_OMIT_AUTOINIT 1