From 0ad4912eb07429b1ea857f8ca06bc7d7ca27acd1 Mon Sep 17 00:00:00 2001 From: mere-human <9664141+mere-human@users.noreply.github.com> Date: Tue, 31 Aug 2021 20:48:24 +0300 Subject: [PATCH] Enhance Insert Custom Date command by using MS datetime format Fix #10467, close #10480 --- PowerEditor/src/MISC/Common/Common.cpp | 109 +++++++++++++++--- PowerEditor/src/Parameters.h | 2 +- .../src/WinControls/Preference/preference.rc | 4 +- 3 files changed, 98 insertions(+), 17 deletions(-) diff --git a/PowerEditor/src/MISC/Common/Common.cpp b/PowerEditor/src/MISC/Common/Common.cpp index 370ddabc4..cb38ce789 100644 --- a/PowerEditor/src/MISC/Common/Common.cpp +++ b/PowerEditor/src/MISC/Common/Common.cpp @@ -1331,25 +1331,106 @@ int nbDigitsFromNbLines(size_t nbLines) return nbDigits; } +namespace +{ + constexpr TCHAR timeFmtEscapeChar = 0x1; + constexpr TCHAR middayFormat[] = _T("tt"); + + // Returns AM/PM string defined by the system locale for the specified time. + // This string may be empty or customized. + generic_string getMiddayString(const TCHAR* localeName, const SYSTEMTIME& st) + { + generic_string midday; + midday.resize(MAX_PATH); + int ret = GetTimeFormatEx(localeName, 0, &st, middayFormat, &midday[0], static_cast(midday.size())); + if (ret > 0) + midday.resize(ret - 1); // Remove the null-terminator. + else + midday.clear(); + return midday; + } + + // Replaces conflicting time format specifiers by a special character. + bool escapeTimeFormat(generic_string& format) + { + bool modified = false; + for (auto& ch : format) + { + if (ch == middayFormat[0]) + { + ch = timeFmtEscapeChar; + modified = true; + } + } + return modified; + } + + // Replaces special time format characters by actual AM/PM string. + void unescapeTimeFormat(generic_string& format, const generic_string& midday) + { + if (midday.empty()) + { + auto it = std::remove(format.begin(), format.end(), timeFmtEscapeChar); + if (it != format.end()) + format.erase(it, format.end()); + } + else + { + size_t i = 0; + while ((i = format.find(timeFmtEscapeChar, i)) != generic_string::npos) + { + if (i + 1 < format.size() && format[i + 1] == timeFmtEscapeChar) + { + // 'tt' => AM/PM + format.erase(i, std::size(middayFormat) - 1); + format.insert(i, midday); + } + else + { + // 't' => A/P + format[i] = midday[0]; + } + } + } + } +} + generic_string getDateTimeStrFrom(const generic_string& dateTimeFormat, const SYSTEMTIME& st) { - generic_string dateTimeStr = dateTimeFormat; - dateTimeStr = stringReplace(dateTimeStr, TEXT("Y"), std::to_wstring(st.wYear)); - wchar_t buf[3]; - _snwprintf(buf, sizeof(buf), TEXT("%02d"), st.wMonth); - dateTimeStr = stringReplace(dateTimeStr, TEXT("M"), buf); + const TCHAR* localeName = LOCALE_NAME_USER_DEFAULT; + const DWORD flags = 0; - _snwprintf(buf, sizeof(buf), TEXT("%02d"), st.wDay); - dateTimeStr = stringReplace(dateTimeStr, TEXT("D"), buf); + constexpr int bufferSize = MAX_PATH; + TCHAR buffer[bufferSize] = {}; + int ret = 0; - _snwprintf(buf, sizeof(buf), TEXT("%02d"), st.wHour); - dateTimeStr = stringReplace(dateTimeStr, TEXT("h"), buf); - _snwprintf(buf, sizeof(buf), TEXT("%02d"), st.wMinute); - dateTimeStr = stringReplace(dateTimeStr, TEXT("m"), buf); + // 1. Escape 'tt' that means AM/PM or 't' that means A/P. + // This is needed to avoid conflict with 'M' date format that stands for month. + generic_string newFormat = dateTimeFormat; + const bool hasMiddayFormat = escapeTimeFormat(newFormat); - _snwprintf(buf, sizeof(buf), TEXT("%02d"), st.wSecond); - dateTimeStr = stringReplace(dateTimeStr, TEXT("s"), buf); + // 2. Format the time (h/m/s/t/H). + ret = GetTimeFormatEx(localeName, flags, &st, newFormat.c_str(), buffer, bufferSize); + if (ret != 0) + { + // 3. Format the date (d/y/g/M). + // Now use the buffer as a format string to process the format specifiers not recognized by GetTimeFormatEx(). + ret = GetDateFormatEx(localeName, flags, &st, buffer, buffer, bufferSize, nullptr); + } - return dateTimeStr; + if (ret != 0) + { + if (hasMiddayFormat) + { + // 4. Now format only the AM/PM string. + const generic_string midday = getMiddayString(localeName, st); + generic_string result = buffer; + unescapeTimeFormat(result, midday); + return result; + } + return buffer; + } + + return {}; } diff --git a/PowerEditor/src/Parameters.h b/PowerEditor/src/Parameters.h index 246efb9df..ac2452db1 100644 --- a/PowerEditor/src/Parameters.h +++ b/PowerEditor/src/Parameters.h @@ -784,7 +784,7 @@ struct NppGUI final generic_string _uriSchemes = TEXT("svn:// cvs:// git:// imap:// irc:// irc6:// ircs:// ldap:// ldaps:// news: telnet:// gopher:// ssh:// sftp:// smb:// skype: snmp:// spotify: steam:// sms: slack:// chrome:// bitcoin:"); NewDocDefaultSettings _newDocDefaultSettings; - generic_string _dateTimeFormat = TEXT("Y-M-D h:m:s"); + generic_string _dateTimeFormat = TEXT("yyyy-MM-dd HH:mm:ss"); bool _dateTimeReverseDefaultOrder = false; void setTabReplacedBySpace(bool b) {_tabReplacedBySpace = b;}; diff --git a/PowerEditor/src/WinControls/Preference/preference.rc b/PowerEditor/src/WinControls/Preference/preference.rc index b83b9e077..4e22112eb 100644 --- a/PowerEditor/src/WinControls/Preference/preference.rc +++ b/PowerEditor/src/WinControls/Preference/preference.rc @@ -389,8 +389,8 @@ BEGIN LTEXT "* The modification of this setting needs to restart Notepad++",IDD_STATIC_RESTARTNOTE,110,70,239,20 GROUPBOX "Customize insert Date Time",IDC_DATETIMEFORMAT_GB_STATIC,90,97,268,105,BS_CENTER CONTROL "Reverse default date time order (short && long formats)",IDD_DATETIMEFORMAT_REVERSEORDER_CHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,103,113,241,10 - RTEXT "Y-M-D h:m:s\nh:m:s D/M/Y\nM D, Y h:m",IDC_STATIC,134,142,77,25 - LTEXT "1985-10-26 01:24:00\n01:24:00 26/10/1985\n10 26, 1985 01:24",IDC_STATIC,234,142,94,25 + RTEXT "yyyy-MM-dd HH:mm:ss\nHH:mm:ss dd/MM/yy\nMMM d, yyyy h:m",IDC_STATIC,134,142,77,25 + LTEXT "1985-10-26 01:24:00\n01:24:00 26/10/85\nOct 26, 1985 1:24",IDC_STATIC,234,142,94,25 RTEXT "Custom format:",IDD_DATETIMEFORMAT_STATIC,92,172,71,8 EDITTEXT IDC_DATETIMEFORMAT_EDIT,164,170,179,14,ES_AUTOHSCROLL LTEXT "",IDD_DATETIMEFORMAT_RESULT_STATIC,166,187,180,8