Files
five/hbrtl/rtl_datetime2.go
Charles KWON OhJun 486e466592 feat: FiveSql2 43/43, @byref, mutable closure, RTL 479, DateTime fix
Major changes since last commit:
- FiveSql2 SQL:1999 engine (10,458 LOC) — 43/43 ALL PASS
- 21 compiler/runtime bugs fixed (short-circuit AND/OR, FOR LOOP, etc.)
- @byref pass-by-reference via RefCell pattern
- Mutable closure capture (EnsureLocalRef + RefCell sharing)
- RTL: 400 → 479 functions (+79: file, string, datetime, hash, UTF-8)
- DateTime/Timestamp fully working (hb_DateTime, hb_Hour/Min/Sec, display)
- Reserved word guard (39 keywords blocked from function calls)
- AEval arg order fix (element before index)
- Closure capture redecl fix (unique _cap_ names per block)
- Hash/string indexing in ArrayPush/ArrayPop
- Harbour compat test suite: 51/51
- 4 docs: Porting Report, Implementation Plan, Optimization Plan, Commercialization

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 11:35:37 +09:00

214 lines
5.7 KiB
Go

// Copyright (c) 2026 Charles KWON OhJun. All rights reserved.
// Extended date/time RTL functions.
package hbrtl
import (
"five/hbrt"
"fmt"
"strings"
"time"
)
// HB_DATE(nYear, nMonth, nDay) → dDate
func HbDate(t *hbrt.Thread) {
t.Frame(3, 0); defer t.EndProc()
y := t.Local(1).AsInt()
m := t.Local(2).AsInt()
d := t.Local(3).AsInt()
t.RetVal(hbrt.MakeDate(dateToJulian(int(y), int(m), int(d))))
}
// HB_CTOD(cDate [, cFormat]) → dDate (extended CTOD with format)
func HbCToD(t *hbrt.Thread) {
nParams := t.ParamCount()
t.Frame(nParams, 0); defer t.EndProc()
s := strings.TrimSpace(t.Local(1).AsString())
if s == "" { t.RetVal(hbrt.MakeDate(0)); return }
// Default format: "YYYY-MM-DD" or "YYYYMMDD"
var gt time.Time
var err error
for _, layout := range []string{"2006-01-02", "20060102", "01/02/2006", "02/01/2006", "2006.01.02"} {
gt, err = time.Parse(layout, s)
if err == nil { break }
}
if err != nil { t.RetVal(hbrt.MakeDate(0)); return }
y, m, d := gt.Date()
t.RetVal(hbrt.MakeDate(dateToJulian(y, int(m), d)))
}
// HB_DTOC(dDate [, cFormat]) → cString (extended DTOC with format)
func HbDToC(t *hbrt.Thread) {
nParams := t.ParamCount()
t.Frame(nParams, 0); defer t.EndProc()
j := t.Local(1).AsJulian()
if j == 0 { t.RetString(""); return }
y, m, d := julianToDate(j)
gt := time.Date(y, time.Month(m), d, 0, 0, 0, 0, time.Local)
layout := "01/02/2006"
if nParams >= 2 && !t.Local(2).IsNil() {
layout = harbourFmtToGo(t.Local(2).AsString())
}
t.RetString(gt.Format(layout))
}
// HB_STOD(cYYYYMMDD) → dDate (alias — STOD already exists, this is hb_ prefixed)
func HbSToD(t *hbrt.Thread) {
t.Frame(1, 0); defer t.EndProc()
s := t.Local(1).AsString()
if len(s) < 8 { t.RetVal(hbrt.MakeDate(0)); return }
gt, err := time.Parse("20060102", s[:8])
if err != nil { t.RetVal(hbrt.MakeDate(0)); return }
y, m, d := gt.Date()
t.RetVal(hbrt.MakeDate(dateToJulian(y, int(m), d)))
}
// HB_DTOT(dDate) → tTimestamp (Date → Timestamp at midnight)
func HbDToT(t *hbrt.Thread) {
t.Frame(1, 0); defer t.EndProc()
t.RetVal(hbrt.MakeTimestamp(t.Local(1).AsJulian(), 0))
}
// HB_TTOD(tTimestamp) → dDate (Timestamp → Date, discard time)
func HbTToD(t *hbrt.Thread) {
t.Frame(1, 0); defer t.EndProc()
t.RetVal(hbrt.MakeDate(t.Local(1).AsJulian()))
}
// HB_TTOHOUR(tTS) → nHour
func HbTToHour(t *hbrt.Thread) {
t.Frame(1, 0); defer t.EndProc()
ms := t.Local(1).AsTimeMs()
t.RetInt(int64(ms / 3600000))
}
// HB_TTOMIN(tTS) → nMinute
func HbTToMin(t *hbrt.Thread) {
t.Frame(1, 0); defer t.EndProc()
ms := t.Local(1).AsTimeMs()
t.RetInt(int64(ms / 60000 % 60))
}
// HB_TTOSEC(tTS) → nSecond
func HbTToSec(t *hbrt.Thread) {
t.Frame(1, 0); defer t.EndProc()
ms := t.Local(1).AsTimeMs()
t.RetInt(int64(ms / 1000 % 60))
}
// HB_TTOMSEC(tTS) → nMillisecond
func HbTToMsec(t *hbrt.Thread) {
t.Frame(1, 0); defer t.EndProc()
ms := t.Local(1).AsTimeMs()
t.RetInt(int64(ms % 1000))
}
// HB_TTON(tTS) → nSeconds (seconds since midnight)
func HbTToN(t *hbrt.Thread) {
t.Frame(1, 0); defer t.EndProc()
ms := t.Local(1).AsTimeMs()
t.RetVal(hbrt.MakeDoubleAuto(float64(ms) / 1000.0))
}
// HB_NTOT(dDate, nSeconds) → tTimestamp
func HbNToT(t *hbrt.Thread) {
t.Frame(2, 0); defer t.EndProc()
j := t.Local(1).AsJulian()
sec := t.Local(2).AsNumDouble()
ms := int32(sec * 1000)
t.RetVal(hbrt.MakeTimestamp(j, ms))
}
// HB_NTOHOUR(nSeconds) → nHour
func HbNToHour(t *hbrt.Thread) {
t.Frame(1, 0); defer t.EndProc()
t.RetInt(int64(t.Local(1).AsNumDouble()) / 3600)
}
// HB_NTOMIN(nSeconds) → nMinute
func HbNToMin(t *hbrt.Thread) {
t.Frame(1, 0); defer t.EndProc()
t.RetInt(int64(t.Local(1).AsNumDouble()) / 60 % 60)
}
// HB_NTOSEC(nSeconds) → nSecond (mod 60)
func HbNToSec(t *hbrt.Thread) {
t.Frame(1, 0); defer t.EndProc()
t.RetInt(int64(t.Local(1).AsNumDouble()) % 60)
}
// HB_WEEK(dDate) → nWeek (ISO week number)
func HbWeek(t *hbrt.Thread) {
t.Frame(1, 0); defer t.EndProc()
j := t.Local(1).AsJulian()
if j == 0 { t.RetInt(0); return }
y, m, d := julianToDate(j)
gt := time.Date(y, time.Month(m), d, 0, 0, 0, 0, time.Local)
_, w := gt.ISOWeek()
t.RetInt(int64(w))
}
// HB_CDAY(nDow) → cDayName (1=Sunday)
func HbCDay(t *hbrt.Thread) {
t.Frame(1, 0); defer t.EndProc()
n := t.Local(1).AsInt()
days := []string{"", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}
if n >= 1 && n <= 7 { t.RetString(days[n]) } else { t.RetString("") }
}
// DAYS(nSeconds) → nDays
func Days(t *hbrt.Thread) {
t.Frame(1, 0); defer t.EndProc()
t.RetInt(int64(t.Local(1).AsNumDouble()) / 86400)
}
// ELAPTIME(cStart, cEnd) → cElapsed "HH:MM:SS"
func ElapTime(t *hbrt.Thread) {
t.Frame(2, 0); defer t.EndProc()
s1 := parseHMS(t.Local(1).AsString())
s2 := parseHMS(t.Local(2).AsString())
diff := s2 - s1
if diff < 0 { diff += 86400 }
h := diff / 3600; m := diff / 60 % 60; s := diff % 60
t.RetString(fmt.Sprintf("%02d:%02d:%02d", h, m, s))
}
// AMPM(cTime) → cTime12h ("13:30" → "01:30 PM")
func AMPM(t *hbrt.Thread) {
t.Frame(1, 0); defer t.EndProc()
s := t.Local(1).AsString()
sec := parseHMS(s)
h := sec / 3600
m := sec / 60 % 60
sc := sec % 60
suffix := "AM"
if h >= 12 { suffix = "PM" }
if h > 12 { h -= 12 }
if h == 0 { h = 12 }
t.RetString(fmt.Sprintf("%02d:%02d:%02d %s", h, m, sc, suffix))
}
// SECS(cTime) → nSeconds ("01:30:00" → 5400)
func Secs(t *hbrt.Thread) {
t.Frame(1, 0); defer t.EndProc()
t.RetInt(int64(parseHMS(t.Local(1).AsString())))
}
// --- helpers ---
func parseHMS(s string) int {
s = strings.TrimSpace(s)
var h, m, sc int
fmt.Sscanf(s, "%d:%d:%d", &h, &m, &sc)
return h*3600 + m*60 + sc
}
func harbourFmtToGo(hFmt string) string {
r := strings.NewReplacer(
"YYYY", "2006", "YY", "06",
"MM", "01", "DD", "02",
"HH", "15", "mm", "04", "SS", "05",
)
return r.Replace(hFmt)
}