Skip to content

Commit

Permalink
Added _dateStr intrinsic for basic date->string conversion.
Browse files Browse the repository at this point in the history
Still need _dateVal, to go the other way (and to update makefiles).
  • Loading branch information
JoeStrout committed Feb 3, 2023
1 parent 1295d0a commit 55074c2
Show file tree
Hide file tree
Showing 4 changed files with 258 additions and 1 deletion.
185 changes: 185 additions & 0 deletions MiniScript-cpp/src/DateTimeUtils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
//
// DateTimeUtils.cpp
// MiniScript
//
// Created by Joe Strout on 2/1/23.
// Copyright © 2023 Joe Strout. All rights reserved.
//

#include "DateTimeUtils.h"
#include "SplitJoin.h"
#include <time.h>
#include <math.h>

namespace MiniScript {

static bool Match(const String s, size_t *posB, const String match) {
size_t matchLenB = match.LengthB();
if (s.SubstringB(*posB, matchLenB) == match) {
*posB += matchLenB;
return true;
}
return false;
}

String FormatDate(time_t t, const String formatSpec) {
tm dateTime;
localtime_r(&t, &dateTime);

const int BUFSIZE = 128;
char buffer[BUFSIZE];
setlocale(LC_ALL, "");

StringList result;
size_t posB = 0, lenB = formatSpec.LengthB();
while (posB < lenB) {
if (Match(formatSpec, &posB, "yyyy")) { // year
result.Add(String::Format(dateTime.tm_year + 1900, "%04d"));
} else if (Match(formatSpec, &posB, "yyy")) {
result.Add(String::Format(dateTime.tm_year + 1900, "%03d"));
} else if (Match(formatSpec, &posB, "yy")) {
result.Add(String::Format(dateTime.tm_year % 100, "%02d"));
} else if (Match(formatSpec, &posB, "MMMM")) { // month name
strftime(buffer, BUFSIZE, "%B", &dateTime);
result.Add(buffer);
} else if (Match(formatSpec, &posB, "MMM")) { // month abbreviation
strftime(buffer, BUFSIZE, "%b", &dateTime);
result.Add(buffer);
} else if (Match(formatSpec, &posB, "MM")) { // month number
result.Add(String::Format(dateTime.tm_mon + 1, "%02d"));
} else if (Match(formatSpec, &posB, "M")) {
result.Add(String::Format(dateTime.tm_mon + 1, "%d"));
} else if (Match(formatSpec, &posB, "dddd")) { // weekday name
strftime(buffer, BUFSIZE, "%A", &dateTime);
result.Add(buffer);
} else if (Match(formatSpec, &posB, "ddd")) { // weekday abbreviation
strftime(buffer, BUFSIZE, "%a", &dateTime);
result.Add(buffer);
} else if (Match(formatSpec, &posB, "dd")) { // day (number)
result.Add(String::Format(dateTime.tm_mday, "%02d"));
} else if (Match(formatSpec, &posB, "d")) {
result.Add(String::Format(dateTime.tm_mday, "%d"));
} else if (Match(formatSpec, &posB, "hh")) { // 12-hour hour
int twelveHourHour = (dateTime.tm_hour == 0 ? 12 : (dateTime.tm_hour - 1) % 12+1);
result.Add(String::Format(twelveHourHour, "%02d"));
} else if (Match(formatSpec, &posB, "h")) {
int twelveHourHour = (dateTime.tm_hour == 0 ? 12 : (dateTime.tm_hour - 1) % 12+1);
result.Add(String::Format(twelveHourHour, "%d"));
} else if (Match(formatSpec, &posB, "HH")) { // 24-hour hour
result.Add(String::Format(dateTime.tm_hour, "%02d"));
} else if (Match(formatSpec, &posB, "H")) {
result.Add(String::Format(dateTime.tm_hour, "%d"));
} else if (Match(formatSpec, &posB, "mm")) { // minute
result.Add(String::Format(dateTime.tm_min, "%02d"));
} else if (Match(formatSpec, &posB, "m")) {
result.Add(String::Format(dateTime.tm_min, "%d"));
} else if (Match(formatSpec, &posB, "ss")) { // second
result.Add(String::Format(dateTime.tm_sec, "%02d"));
} else if (Match(formatSpec, &posB, "s")) {
result.Add(String::Format(dateTime.tm_sec, "%d"));
} else if (Match(formatSpec, &posB, "ffffff")) { // fractional part of seconds value
double d, i;
d = modf(t, &i);
snprintf(buffer, BUFSIZE, "%.6f", d);
result.Add(&buffer[2]); // (skipping past "0.")
} else if (Match(formatSpec, &posB, "fffff")) {
double d, i;
d = modf(t, &i);
snprintf(buffer, BUFSIZE, "%.5f", d);
result.Add(&buffer[2]); // (skipping past "0.")
} else if (Match(formatSpec, &posB, "ffff")) {
double d, i;
d = modf(t, &i);
snprintf(buffer, BUFSIZE, "%.4f", d);
result.Add(&buffer[2]); // (skipping past "0.")
} else if (Match(formatSpec, &posB, "fff")) {
double d, i;
d = modf(t, &i);
snprintf(buffer, BUFSIZE, "%.3f", d);
result.Add(&buffer[2]); // (skipping past "0.")
} else if (Match(formatSpec, &posB, "ff")) {
double d, i;
d = modf(t, &i);
snprintf(buffer, BUFSIZE, "%.2f", d);
result.Add(&buffer[2]); // (skipping past "0.")
} else if (Match(formatSpec, &posB, "f")) {
double d, i;
d = modf(t, &i);
snprintf(buffer, BUFSIZE, "%.1f", d);
result.Add(&buffer[2]); // (skipping past "0.")
} else if (Match(formatSpec, &posB, "FFFFFF")) { // fractional part of seconds value, if nonzero
double d, i;
d = modf(t, &i);
snprintf(buffer, BUFSIZE, "%.6f", d);
String s(&buffer[2]); // (skipping past "0.")
if (s != "000000") result.Add(s);
} else if (Match(formatSpec, &posB, "FFFFF")) {
double d, i;
d = modf(t, &i);
snprintf(buffer, BUFSIZE, "%.5f", d);
String s(&buffer[2]); // (skipping past "0.")
if (s != "00000") result.Add(s);
} else if (Match(formatSpec, &posB, "FFFF")) {
double d, i;
d = modf(t, &i);
snprintf(buffer, BUFSIZE, "%.4f", d);
String s(&buffer[2]); // (skipping past "0.")
if (s != "0000") result.Add(s);
} else if (Match(formatSpec, &posB, "FFF")) {
double d, i;
d = modf(t, &i);
snprintf(buffer, BUFSIZE, "%.3f", d);
String s(&buffer[2]); // (skipping past "0.")
if (s != "000") result.Add(s);
} else if (Match(formatSpec, &posB, "FF")) {
double d, i;
d = modf(t, &i);
snprintf(buffer, BUFSIZE, "%.2f", d);
String s(&buffer[2]); // (skipping past "0.")
if (s != "00") result.Add(s);
} else if (Match(formatSpec, &posB, "F")) {
double d, i;
d = modf(t, &i);
snprintf(buffer, BUFSIZE, "%.1f", d);
String s(&buffer[2]); // (skipping past "0.")
if (s != "0") result.Add(s);
} else if (Match(formatSpec, &posB, "tt")) { // AM/PM
strftime(buffer, BUFSIZE, "%p", &dateTime);
result.Add(buffer);
} else if (Match(formatSpec, &posB, "t")) {
strftime(buffer, BUFSIZE, "%p", &dateTime);
buffer[1] = 0; // (truncate to length 1)
result.Add(buffer);
} else if (Match(formatSpec, &posB, "gg") || Match(formatSpec, &posB, "g")) {
if (dateTime.tm_year + 1900 > 0) result.Add("A.D.");
else result.Add("B.C.");
} else if (Match(formatSpec, &posB, ":")) {
// Ugh. Getting these in C is hard. For now we're going to punt.
result.Add(":");
} else if (Match(formatSpec, &posB, "/")) {
// See comment above.
result.Add("/");
} else if (formatSpec[posB] == '"') {
// Find the closing quote, and output the contained string literal
size_t endPosB = posB + 1;
while (endPosB < lenB) {
char c = formatSpec[endPosB];
if (c == '\\') endPosB++;
else if (c == '"') break;
}
result.Add(formatSpec.Substring(posB + 1, endPosB - endPosB - 1));
posB = endPosB + 1;
} else if (Match(formatSpec, &posB, "\\") && posB < lenB) {
result.Add(formatSpec[posB++]);
} else {
result.Add(formatSpec.SubstringB(posB, 1));
posB++;
}
}

return Join("", result);
}


}

26 changes: 26 additions & 0 deletions MiniScript-cpp/src/DateTimeUtils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// DateTimeUtils.hpp
// MiniScript
//
// Created by Joe Strout on 2/1/23.
// Copyright © 2023 Joe Strout. All rights reserved.
//

#ifndef DateTimeUtils_hpp
#define DateTimeUtils_hpp

#include <stdio.h>
#include <time.h>
#include "SimpleString.h"

namespace MiniScript {

String FormatDate(time_t dateTime, const String formatSpec);

inline String FormatDate(time_t dateTime) {
return FormatDate(dateTime, "yyyy-MM-dd HH:mm:ss");
}

}

#endif /* DateTimeUtils_hpp */
42 changes: 41 additions & 1 deletion MiniScript-cpp/src/ShellIntrinsics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@
#include "OstreamSupport.h"
#include "MiniScript/SplitJoin.h"
#include "whereami.h"
#include "DateTimeUtils.h"

#include <stdio.h>
#include <time.h>
#if _WIN32 || _WIN64
#define WINDOWS 1
#include <windows.h>
#include <Shlwapi.h>
#include <Fileapi.h>
#include <direct.h>
#include <time.h>
#define getcwd _getcwd
#define setenv _setenv
#else
Expand Down Expand Up @@ -317,6 +318,39 @@ static IntrinsicResult intrinsic_exists(Context *context, IntrinsicResult partia
return IntrinsicResult(Value::Truth(found));
}

static double dateTimeEpoch() {
static double _result = 0;
if (_result == 0) {
tm baseDate;
memset(&baseDate, 0, sizeof(tm));
baseDate.tm_year = 2000 - 1900; // (because tm_year is years since 1900!)
baseDate.tm_mon = 0;
baseDate.tm_mday = 1;
_result = mktime(&baseDate);
}
return _result;
}

static IntrinsicResult intrinsic_dateStr(Context *context, IntrinsicResult partialResult) {
Value date = context->GetVar("date");
Value format = context->GetVar("format");
String formatStr;
if (format.IsNull()) formatStr = "yyyy-MM-dd HH:mm:ss";
else formatStr = format.ToString();
double d;
if (date.IsNull()) {
time_t t;
time(&t);
d = t;
} else if (date.type == ValueType::Number) {
d = date.DoubleValue() + dateTimeEpoch();
} else {
// ToDo: parse a date string
d = 0;
}
return IntrinsicResult(FormatDate((time_t)d, formatStr));
}

static String timestampToString(const struct tm& t) {
String result = String::Format(1900 + t.tm_year) + "-";
if (t.tm_mon < 10) result += "0";
Expand Down Expand Up @@ -858,6 +892,11 @@ void AddShellIntrinsics() {
f = Intrinsic::Create("file");
f->code = &intrinsic_File;

f = Intrinsic::Create("_dateStr");
f->AddParam("date");
f->AddParam("format", "yyyy-MM-dd HH:mm:ss");
f->code = &intrinsic_dateStr;

i_getcwd = Intrinsic::Create("");
i_getcwd->code = &intrinsic_getcwd;

Expand Down Expand Up @@ -948,4 +987,5 @@ void AddShellIntrinsics() {
i_writeLines->AddParam("path");
i_writeLines->AddParam("lines");
i_writeLines->code = &intrinsic_writeLines;

}
6 changes: 6 additions & 0 deletions MiniScript-cpp/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "OstreamSupport.h"
#include "MiniScript/SplitJoin.h"
#include "ShellIntrinsics.h"
#include "DateTimeUtils.h" // TEMP for initial testing

using namespace MiniScript;

Expand Down Expand Up @@ -277,6 +278,11 @@ int main(int argc, const char * argv[]) {
AddScriptPathVar("");
AddShellIntrinsics();

// HACK for initial testing:
time_t t;
time(&t);
Print(FormatDate(t, "MMM d, yyyy \\h hh:mm:ss.FF t"));

for (int i=1; i<argc; i++) {
String arg = argv[i];
if (arg == "-h" or arg == "-?" or arg == "--help") {
Expand Down

0 comments on commit 55074c2

Please sign in to comment.