diff --git a/src/common/Gdc2/Font/Font.cpp b/src/common/Gdc2/Font/Font.cpp --- a/src/common/Gdc2/Font/Font.cpp +++ b/src/common/Gdc2/Font/Font.cpp @@ -1,1447 +1,1454 @@ /*hdr ** FILE: LFont.cpp ** AUTHOR: Matthew Allen ** DATE: 5/5/97 ** DESCRIPTION: Gdc2 Font Support ** ** Copyright (C) 1997-2002, Matthew Allen ** fret@memecode.com */ #ifndef WIN32 #define _WIN32_WINNT 0x500 #endif ////////////////////////////////////////////////////////////////////// // Includes #include #include #include #include #include "lgi/common/Lgi.h" #include "lgi/common/Variant.h" #include "lgi/common/DisplayString.h" #include "lgi/common/GdiLeak.h" #include "lgi/common/Css.h" #include "FontPriv.h" #ifdef FontChange #undef FontChange #endif ////////////////////////////////////////////////////////////////////// // Helpers #if defined(LGI_SDL) class FreetypeLib { FT_Library lib; FT_Error err; public: FreetypeLib() { err = FT_Init_FreeType(&lib); if (err) { LAssert(0); } } ~FreetypeLib() { if (!err) { FT_Done_FreeType(lib); } } FT_Library Handle() { return lib; } LString GetVersion() { FT_Int amajor = 0, aminor = 0, apatch = 0; FT_Library_Version(lib, &amajor, &aminor, &apatch); LString s; s.Printf("%i.%i.%i", amajor, aminor, apatch); return s; } } Freetype2; LString GetFreetypeLibraryVersion() { return Freetype2.GetVersion(); } #elif defined(WIN32) #ifndef __GNUC__ #include #endif int WinPointToHeight(int Pt, HDC hDC) { int Ht = 0; HWND hDestktop = NULL; if (!hDC) hDC = GetDC(hDestktop = GetDesktopWindow()); if (hDC) Ht = -MulDiv(Pt, GetDeviceCaps(hDC, LOGPIXELSY), 72); if (hDestktop) ReleaseDC(hDestktop, hDC); return Ht; } int WinHeightToPoint(int Ht, HDC hDC) { int Pt = 0; HWND hDestktop = NULL; if (!hDC) hDC = GetDC(hDestktop = GetDesktopWindow()); if (hDC) Pt = -MulDiv(Ht, 72, GetDeviceCaps(hDC, LOGPIXELSY)); if (hDestktop) ReleaseDC(hDestktop, hDC); return Pt; } #elif defined(__GTK_H__) #include #elif USE_CORETEXT // CTFontCreateUIFontForLanguage // #include #include #endif //////////////////////////////////////////////////////////////////// #ifdef WINDOWS LAutoPtr LFontPrivate::Gdi32; #endif LFont::LFont(const char *face, LCss::Len size) { d = new LFontPrivate; if (face && size.IsValid()) { Create(face, size); } } LFont::LFont(OsFont Handle) { d = new LFontPrivate; LFontType Type; if (Type.GetFromRef(Handle)) { Create(&Type); } } LFont::LFont(LFontType &Type) { d = new LFontPrivate; Create(&Type); } LFont::LFont(LFont &Fnt) { d = new LFontPrivate; *this = Fnt; } LFont::~LFont() { LAssert(d->WarnOnDelete == false); Destroy(); DeleteObj(d); } bool LFont::CreateFromCss(const char *Css) { if (!Css) return false; LCss c; c.Parse(Css); return CreateFromCss(&c); } bool LFont::CreateFromCss(LCss *Css) { if (!Css) return false; LCss::StringsDef Fam = Css->FontFamily(); if (Fam.Length()) Face(Fam[0]); else Face(LSysFont->Face()); LCss::Len Sz = Css->FontSize(); switch (Sz.Type) { case LCss::SizeSmaller: Size(LCss::Len(LCss::LenPt, (float)LSysFont->PointSize()-1)); break; case LCss::SizeLarger: Size(LCss::Len(LCss::LenPt, (float)LSysFont->PointSize()+1)); break; case LCss::LenInherit: Size(LSysFont->Size()); break; default: Size(Sz); break; } LCss::FontWeightType w = Css->FontWeight(); if (w == LCss::FontWeightBold) Bold(true); LCss::FontStyleType s = Css->FontStyle(); if (s == LCss::FontStyleItalic) Italic(true); LCss::TextDecorType dec = Css->TextDecoration(); if (dec == LCss::TextDecorUnderline) Underline(true); return Create(); } LString LFont::FontToCss() { LCss c; c.FontFamily(Face()); c.FontSize(Size()); if (Bold()) c.FontWeight(LCss::FontWeightBold); if (Italic()) c.FontStyle(LCss::FontStyleItalic); auto aStr = c.ToString(); return aStr.Get(); } void LFont::WarnOnDelete(bool w) { d->WarnOnDelete = w; } bool LFont::Destroy() { bool Status = true; if (d->hFont) { #if LGI_SDL FT_Done_Face(d->hFont); #elif defined(WIN32) DeleteObject(d->hFont); #elif defined __GTK_H__ if (d->PangoCtx) { g_object_unref(d->PangoCtx); d->PangoCtx = NULL; } Gtk::pango_font_description_free(d->hFont); #elif defined MAC #if USE_CORETEXT CFRelease(d->hFont); #else ATSUDisposeStyle(d->hFont); #endif #elif defined(HAIKU) DeleteObj(d->hFont); #else LAssert(0); #endif d->hFont = 0; } DeleteArray(d->GlyphMap); return Status; } #if USE_CORETEXT CFDictionaryRef LFont::GetAttributes() { return d->Attributes; } #endif uchar *LFont::GetGlyphMap() { return d->GlyphMap; } bool LFont::GetOwnerUnderline() { return d->OwnerUnderline; } void LFont::_OnPropChange(bool FontChange) { if (FontChange) { Destroy(); } } OsFont LFont::Handle() { return d->hFont; } int LFont::GetHeight() { if (!d->hFont) { Create(); } // I've decided for the moment to allow zero pt fonts to make a HTML test case render correctly. // LAssert(d->Height != 0); return d->Height; } bool LFont::IsValid() { bool Status = false; // recreate font #ifdef WIN32 if (!d->hFont) { Status = Create(Face(), Size()); } #else if (d->Dirty) { Status = Create(Face(), Size()); d->Dirty = false; } #endif else { Status = true; } return Status; } #ifdef WIN32 #define ENCODING_TABLE_SIZE 8 class type_4_cmap { public: uint16 format; uint16 length; uint16 language; uint16 seg_count_x_2; uint16 search_range; uint16 entry_selector; uint16 range_shift; // uint16 reserved; uint16 arrays[1]; uint16 SegCount() { return seg_count_x_2 / 2; } uint16 *GetIdRangeOffset() { return arrays + 1 + (SegCount()*3); } uint16 *GetStartCount() { return arrays + 1 + SegCount(); } uint16 *GetEndCount() { /* Apparently the reseved spot is not reserved for the end_count array!? */ return arrays; } }; class cmap_encoding_subtable { public: uint16 platform_id; uint16 encoding_id; uint32_t offset; }; #define INT16_SWAP(i) ( ((i)>>8) | (((i)&0xff)<<8) ) #define INT32_SWAP(i) ( ( ((i)&0x000000ff) << 24) | \ ( ((i)&0x0000ff00) << 8 ) | \ ( ((i)&0x00ff0000) >> 8 ) | \ ( ((i)&0xff000000) >> 24) ) #define MAKE_TT_TABLE_NAME(c1, c2, c3, c4) \ (((uint32_t)c4) << 24 | ((uint32_t)c3) << 16 | ((uint32_t)c2) << 8 | ((uint32_t)c1)) #define CMAP (MAKE_TT_TABLE_NAME('c','m','a','p')) #define CMAP_HEADER_SIZE 4 #define APPLE_UNICODE_PLATFORM_ID 0 #define MACINTOSH_PLATFORM_ID 1 #define ISO_PLATFORM_ID 2 #define MICROSOFT_PLATFORM_ID 3 #define SYMBOL_ENCODING_ID 0 #define UNICODE_ENCODING_ID 1 #define UCS4_ENCODING_ID 10 type_4_cmap *GetUnicodeTable(HFONT hFont, uint16_t &Length) { bool Status = false; type_4_cmap *Table = 0; HDC hDC = GetDC(0); if (hDC) { HFONT Old = (HFONT)SelectObject(hDC, hFont); uint16_t Tables = 0; uint32_t Offset = 0; // Get The number of encoding tables, at offset 2 auto Res = GetFontData(hDC, CMAP, 2, &Tables, sizeof(uint16_t)); if (Res == sizeof (uint16_t)) { Tables = INT16_SWAP(Tables); cmap_encoding_subtable *Sub = (cmap_encoding_subtable*)malloc(ENCODING_TABLE_SIZE*Tables); if (Sub) { Res = GetFontData(hDC, CMAP, CMAP_HEADER_SIZE, Sub, ENCODING_TABLE_SIZE*Tables); if (Res == ENCODING_TABLE_SIZE*Tables) { for (int i = 0; i < Tables; i++) { Sub[i].platform_id = INT16_SWAP(Sub[i].platform_id); Sub[i].encoding_id = INT16_SWAP(Sub[i].encoding_id); Sub[i].offset = INT32_SWAP(Sub[i].offset); if (Sub[i].platform_id == MICROSOFT_PLATFORM_ID && Sub[i].encoding_id == UNICODE_ENCODING_ID) { Offset = Sub[i].offset; } } } free(Sub); } } if (Offset) { Length = 0; uint Res = GetFontData(hDC, CMAP, Offset + 2, &Length, sizeof(uint16_t)); if (Res == sizeof(uint16)) { Length = INT16_SWAP(Length); Table = (type_4_cmap*)malloc(Length); Res = GetFontData(hDC, CMAP, Offset, Table, Length); if (Res == Length) { Table->format = INT16_SWAP(Table->format); Table->length = INT16_SWAP(Table->length); Table->language = INT16_SWAP(Table->language); Table->seg_count_x_2 = INT16_SWAP(Table->seg_count_x_2); Table->search_range = INT16_SWAP(Table->search_range); Table->entry_selector = INT16_SWAP(Table->entry_selector); Table->range_shift = INT16_SWAP(Table->range_shift); if (Table->format == 4) { uint16 *tbl_end = (uint16 *)((char *)Table + Length); uint16 *tbl = Table->arrays; while (tbl < tbl_end) { *tbl = INT16_SWAP(*tbl); tbl++; } Status = true; } } } } SelectObject(hDC, Old); ReleaseDC(0, hDC); } if (!Status) { free(Table); Table = 0; } return Table; } #endif LSurface *LFont::GetSurface() { return d->pSurface; } bool LFont::Create(const char *face, LCss::Len size, LSurface *pSurface) { bool FaceChanging = false; bool SizeChanging = false; bool ValidInitFaceSize = ValidStr(Face()) && Size().IsValid(); if (face) { if (!Face() || strcmp(Face(), face) != 0) { FaceChanging = true; } Face(face); } if (size.IsValid()) { SizeChanging = LTypeFace::d->_Size != size; LTypeFace::d->_Size = size; } if ((SizeChanging || FaceChanging) && this == LSysFont && ValidInitFaceSize) { LgiTrace("Warning: Changing sysfont definition.\n"); } if (this == LSysFont) { printf("Setting sysfont up '%s' %i\n", Face(), PointSize()); } #if LGI_SDL LString FaceName; #if defined(WIN32) const char *Ext = "ttf"; LString FontPath = "c:\\Windows\\Fonts"; #elif defined(LINUX) const char *Ext = "ttf"; LString FontPath = "/usr/share/fonts/truetype"; #elif defined(MAC) const char *Ext = "ttc"; LString FontPath = "/System/Library/Fonts"; #else #error "Put your font path here" #endif LFile::Path p = FontPath.Get(); FaceName.Printf("%s.%s", Face(), Ext); p += FaceName; LString Full = p.GetFull(); if (!LFileExists(Full)) { LArray Files; LArray Extensions; LString Pattern; Pattern.Printf("*.%s", Ext); Extensions.Add(Pattern.Get()); LRecursiveFileSearch(FontPath, &Extensions, &Files, NULL, NULL, NULL, NULL); char *Match = NULL; for (unsigned i=0; ihFont); if (error) { LgiTrace("%s:%i - FT_New_Face failed with %i\n", _FL, error); } else { auto Dpi = LScreenDpi(); int PtSize = PointSize(); int PxSize = (int) (PtSize * Dpi.x / 72.0); error = FT_Set_Char_Size( d->hFont, /* handle to face object */ 0, /* char_width in 1/64th of points */ PtSize*64, /* char_height in 1/64th of points */ Dpi, /* horizontal device resolution */ Dpi); if (error) { LgiTrace("%s:%i - FT_Set_Char_Size failed with %i\n", _FL, error); } d->Height = (int) (ceil((double)d->hFont->height * PxSize / d->hFont->units_per_EM) + 0.0001); LTypeFace::d->_Ascent = (double)d->hFont->ascender * PxSize / d->hFont->units_per_EM; LAssert(d->Height > LTypeFace::d->_Ascent); LTypeFace::d->_Descent = d->Height - LTypeFace::d->_Ascent; return true; } #elif WINNATIVE if (d->hFont) { DeleteObject(d->hFont); d->hFont = NULL; } d->pSurface = pSurface; HDC hDC = pSurface ? pSurface->Handle() : GetDC(0); auto Sz = Size(); int Win32Height = 0; if (Sz.Type == LCss::LenPt) { + if (Sz.Value == 0) + return false; Win32Height = WinPointToHeight((int)Sz.Value, hDC); } else if (Sz.Type == LCss::LenPx) { if (Sz.Value == 0) return false; Win32Height = (int)(Sz.Value * 1.2); } else { LFontType def; if (def.GetSystemFont("System")) { switch (Sz.Type) { case LCss::SizeXXSmall: Win32Height = WinPointToHeight(def.GetPointSize() - 3, hDC); break; case LCss::SizeXSmall: Win32Height = WinPointToHeight(def.GetPointSize() - 2, hDC); break; case LCss::SizeSmall: Win32Height = WinPointToHeight(def.GetPointSize() - 1, hDC); break; case LCss::SizeMedium: Win32Height = WinPointToHeight(def.GetPointSize(), hDC); break; case LCss::SizeLarge: Win32Height = WinPointToHeight(def.GetPointSize() + 1, hDC); break; case LCss::SizeXLarge: Win32Height = WinPointToHeight(def.GetPointSize() + 2, hDC); break; case LCss::SizeXXLarge: Win32Height = WinPointToHeight(def.GetPointSize() + 3, hDC); break; default: LAssert(!"Impl me."); break; } } } - if (Win32Height == 0) + if (Win32Height == 0) + { + LStringPipe p; + Sz.ToString(p); + LgiTrace("%s:%i - Couldn't resolve css='%s' to a valid height.\n", _FL, p.NewLStr().Get()); LAssert(!"What now?"); + } LTypeFace::d->IsSymbol = LTypeFace::d->_Face && ( stristr(LTypeFace::d->_Face, "wingdings") || stristr(LTypeFace::d->_Face, "symbol") ); int Cs; if (LTypeFace::d->IsSymbol) Cs = SYMBOL_CHARSET; else Cs = ANSI_CHARSET; d->OwnerUnderline = Face() && stricmp(Face(), "Courier New") == 0 && Size().Type == LCss::LenPt && (PointSize() == 8 || PointSize() == 9) && LTypeFace::d->_Underline; LAutoWString wFace(Utf8ToWide(LTypeFace::d->_Face)); if (Win32Height) d->hFont = ::CreateFont(Win32Height, 0, 0, 0, LTypeFace::d->_Weight, LTypeFace::d->_Italic, d->OwnerUnderline ? false : LTypeFace::d->_Underline, false, Cs, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, LTypeFace::d->_Quality, FF_DONTCARE, wFace); if (d->hFont) { HFONT hFnt = (HFONT) SelectObject(hDC, d->hFont); TEXTMETRIC tm; ZeroObj(tm); if (GetTextMetrics(hDC, &tm)) { d->Height = tm.tmHeight; LTypeFace::d->_Ascent = tm.tmAscent; LTypeFace::d->_Descent = tm.tmDescent; LTypeFace::d->_Leading = tm.tmInternalLeading; } else { SIZE Size = {0, 0}; GetTextExtentPoint32(hDC, L"Ag", 2, &Size); d->Height = Size.cy; } uint Bytes = (MAX_UNICODE + 1) >> 3; if (!d->GlyphMap) { d->GlyphMap = new uchar[Bytes]; } if (d->GlyphMap) { memset(d->GlyphMap, 0, Bytes); LArray OsVer; int OsType = LGetOs(&OsVer); if (OsType == LGI_OS_WIN9X || OsVer[0] < 5) // GetFontUnicodeRanges is supported on >= Win2k { bool HideUnihan = false; LAssert(sizeof(type_4_cmap)==16); uint16 Length = 0; type_4_cmap *t = GetUnicodeTable(Handle(), Length); if (t) { uint16 SegCount = t->seg_count_x_2 / 2; uint16 *EndCount = t->GetEndCount(); uint16 *StartCount = t->GetStartCount(); uint16 *IdRangeOffset = t->GetIdRangeOffset(); for (int i = 0; i= 0x3400 && u <= 0x9FAF) { // APPROXIMATE } else { // EXACT } if ((u >> 3) < Bytes) { d->GlyphMap[u>>3] |= 1 << (u & 7); } } } else { uint16 *End = (uint16*) (((char*)t)+Length); ssize_t IdBytes = End - IdRangeOffset; for (uint u = StartCount[i]; u <= EndCount[i] && IdRangeOffset[i] != 0xffff; u++) { uint id = *(IdRangeOffset[i]/2 + (u - StartCount[i]) + &IdRangeOffset[i]); if (id) { if (HideUnihan && u >= 0x3400 && u <= 0x9FAF) { // APPROXIMATE } else { // EXACT } if ((u >> 3) < Bytes) { d->GlyphMap[u>>3] |= 1 << (u & 7); } } } } } } else { // not a TTF? assume that it can handle 00-ff in the current ansi cp /* char *Cp = LAnsiToLgiCp(); for (int i=0; i<=0x7f; i++) { char16 u; uchar c = i; void *In = &c; int Size = sizeof(c); LBufConvertCp(&u, "ucs-2", sizeof(u), In, Cp, Size); if ((u >> 3) < Bytes) { GlyphMap[u>>3] |= 1 << (u & 7); } } */ } } else { typedef DWORD (WINAPI *Proc_GetFontUnicodeRanges)(HDC, LPGLYPHSET); if (!d->Gdi32) d->Gdi32.Reset(new LLibrary("Gdi32")); if (d->Gdi32) { Proc_GetFontUnicodeRanges GetFontUnicodeRanges = (Proc_GetFontUnicodeRanges)d->Gdi32->GetAddress("GetFontUnicodeRanges"); if (GetFontUnicodeRanges) { DWORD BufSize = GetFontUnicodeRanges(hDC, 0); LPGLYPHSET Set = (LPGLYPHSET) new char[BufSize]; if (Set && GetFontUnicodeRanges(hDC, Set) > 0) { for (DWORD i=0; icRanges; i++) { WCRANGE *Range = Set->ranges + i; for (int n=0; ncGlyphs; n++) { DWORD u = Range->wcLow + n; if (u >> 3 < Bytes) { d->GlyphMap[u>>3] |= 1 << (u & 7); } } } } DeleteArray((char*&)Set); } } if (LTypeFace::d->IsSymbol) { // Lies! It's all Lies! Symbol doesn't support non-breaking space. int u = 0xa0; d->GlyphMap[u >> 3] &= ~(1 << (u & 7)); } } if (d->GlyphMap) { memset(d->GlyphMap, 0xff, 128/8); } } SelectObject(hDC, hFnt); } if (!pSurface) ReleaseDC(0, hDC); return (d->hFont != 0); #elif defined __GTK_H__ Destroy(); auto Dpi = pSurface ? pSurface->GetDpi() : LScreenDpi(); float DpiScale = (float)Dpi.x / 96.0f; d->hFont = Gtk::pango_font_description_new(); if (!d->hFont) printf("%s:%i - pango_font_description_new failed: Face='%s' Size=%i Bold=%i Italic=%i\n", _FL, Face(), PointSize(), Bold(), Italic()); else if (!ValidStr(Face())) printf("%s:%i - No font face.\n", _FL); else if (!Size().IsValid()) printf("%s:%i - Invalid size.\n", _FL); else { auto Sz = Size(); LString sFace = Face(); Gtk::pango_font_description_set_family(d->hFont, sFace); if (Sz.Type == LCss::LenPt) Gtk::pango_font_description_set_size(d->hFont, Sz.Value * DpiScale * PANGO_SCALE); else if (Sz.Type == LCss::LenPx) Gtk::pango_font_description_set_absolute_size(d->hFont, Sz.Value * DpiScale * PANGO_SCALE); else { LAssert(0); return false; } if (Bold()) Gtk::pango_font_description_set_weight(d->hFont, Gtk::PANGO_WEIGHT_BOLD); // Get metrics for this font... Gtk::GtkPrintContext *PrintCtx = pSurface ? pSurface->GetPrintContext() : NULL; Gtk::PangoContext *SysCtx = LFontSystem::Inst()->GetContext(); if (PrintCtx) d->PangoCtx = gtk_print_context_create_pango_context(PrintCtx); auto EffectiveCtx = d->PangoCtx ? d->PangoCtx : SysCtx; Gtk::PangoFontMetrics *m = Gtk::pango_context_get_metrics(d->PangoCtx ? d->PangoCtx : SysCtx, d->hFont, 0); if (!m) printf("pango_font_get_metrics failed.\n"); else { LTypeFace::d->_Ascent = (double)Gtk::pango_font_metrics_get_ascent(m) / PANGO_SCALE; LTypeFace::d->_Descent = (double)Gtk::pango_font_metrics_get_descent(m) / PANGO_SCALE; d->Height = ceil(LTypeFace::d->_Ascent + LTypeFace::d->_Descent + 1/*hack the underscores to work*/); #if 0 if (PrintCtx) { LgiTrace("LFont::Create %s,%f (%i,%i,%i) (%.1f,%.1f,%i)\n", Gtk::pango_font_description_get_family(d->hFont), (double)Gtk::pango_font_description_get_size(d->hFont) / PANGO_SCALE, Gtk::pango_font_metrics_get_ascent(m), Gtk::pango_font_metrics_get_descent(m), PANGO_SCALE, LTypeFace::d->_Ascent, LTypeFace::d->_Descent, d->Height); } #endif Gtk::pango_font_metrics_unref(m); #if 1 Gtk::PangoFont *fnt = pango_font_map_load_font(Gtk::pango_cairo_font_map_get_default(), EffectiveCtx, d->hFont); if (fnt) { Gtk::PangoCoverage *c = Gtk::pango_font_get_coverage(fnt, Gtk::pango_language_get_default()); if (c) { uint Bytes = (MAX_UNICODE + 1) >> 3; if ((d->GlyphMap = new uchar[Bytes])) { memset(d->GlyphMap, 0, Bytes); int Bits = Bytes << 3; for (int i=0; iGlyphMap[i>>3] |= 1 << (i & 0x7); } } Gtk::pango_coverage_unref(c); } g_object_unref(fnt); } #endif return true; } } #elif defined MAC Destroy(); if (this == LSysFont) LgiTrace("%s:%i - WARNING: you are re-creating the system font... this is bad!!!!\n", _FL); if (Face()) { if (d->Attributes) CFRelease(d->Attributes); auto Sz = Size(); LString sFamily(Face()); LString sBold("Bold"); LArray key; LArray values; key.Add(kCTFontFamilyNameAttribute); values.Add(sFamily.CreateStringRef()); if (!values[0]) return false; if (Bold()) { key.Add(kCTFontStyleNameAttribute); values.Add(sBold.CreateStringRef()); } CFDictionaryRef FontAttrD = CFDictionaryCreate( kCFAllocatorDefault, (const void**)key.AddressOf(), (const void**)values.AddressOf(), key.Length(), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (FontAttrD) { CTFontDescriptorRef descriptor = CTFontDescriptorCreateWithAttributes(FontAttrD); if (descriptor) { float PtSz = 0.0; if (Sz.Type == LCss::LenPt) PtSz = Sz.Value; else if (Sz.Type == LCss::LenPx) // This seems to give fonts that are too small: // PtSz = Sz.Value * 72.0f / LScreenDpi(); PtSz = Sz.Value; else LAssert(!"Impl me."); d->hFont = CTFontCreateWithFontDescriptor(descriptor, PtSz, NULL); CFRelease(descriptor); } else LAssert(0); CFRelease(FontAttrD); } else { LAssert(0); return false; } for (size_t i=0; ihFont) { LTypeFace::d->_Ascent = CTFontGetAscent(d->hFont); LTypeFace::d->_Descent = CTFontGetDescent(d->hFont); LTypeFace::d->_Leading = CTFontGetLeading(d->hFont); d->Height = ceil(LTypeFace::d->_Ascent + LTypeFace::d->_Descent + LTypeFace::d->_Leading); #if 0 if (Sz.Type == LCss::LenPx) { LStringPipe p; Sz.ToString(p); LgiTrace("%s:%i - LFont::Create(%s,%s) = %f,%f,%f (%i)\n", _FL, Face(), p.NewLStr().Get(), LTypeFace::d->_Ascent, LTypeFace::d->_Descent, LTypeFace::d->_Leading, GetHeight()); } #endif key.Add(kCTFontAttributeName); values.Add(d->hFont); key.Add(kCTForegroundColorFromContextAttributeName); values.Add(kCFBooleanTrue); if (Underline()) { key.Add(kCTUnderlineStyleAttributeName); CTUnderlineStyle u = kCTUnderlineStyleSingle; values.Add(CFNumberCreate(NULL, kCFNumberSInt32Type, &u)); } d->Attributes = CFDictionaryCreate( kCFAllocatorDefault, (const void**)key.AddressOf(), (const void**)values.AddressOf(), key.Length(), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); return true; } for (int i=2; ihFont = new BFont(); d->hFont->SetSize(PointSize()); status_t r = d->hFont->SetFamilyAndStyle(Face(), "Regular"); // printf("SetFamilyAndFace(%s)=%i\n", Face(), r); if (r == B_OK) { font_height height; d->hFont->GetHeight(&height); d->Height = height.ascent + height.descent + height.leading; LTypeFace::d->_Ascent = height.ascent; LTypeFace::d->_Descent = height.descent; LTypeFace::d->_Leading = height.leading; return true; } #endif return false; } char16 *LFont::_ToUnicode(char *In, ssize_t &Len) { char16 *WStr = 0; if (In && Len > 0) { WStr = (char16*)LNewConvertCp(LGI_WideCharset, In, LTypeFace::d->_CodePage, Len); if (WStr) { ssize_t l = StrlenW(WStr); if (l < Len) { Len = l; } } } return WStr; } #if defined WINNATIVE bool LFont::Create(LFontType *LogFont, LSurface *pSurface) { if (d->hFont) { DeleteObject(d->hFont); d->hFont = 0; } if (LogFont) { // set props PointSize(WinHeightToPoint(LogFont->Info.lfHeight)); LString uFace = LogFont->Info.lfFaceName; if (ValidStr(uFace)) { Face(uFace); Quality(LogFont->Info.lfQuality); Bold(LogFont->Info.lfWeight >= FW_BOLD); Italic(LogFont->Info.lfItalic != FALSE); Underline(LogFont->Info.lfUnderline != FALSE); // create the handle Create(0, 0, pSurface); } } return (d->hFont != 0); } #else bool LFont::Create(LFontType *LogFont, LSurface *pSurface) { if (LogFont) { LCss::Len Sz(LCss::LenPt, (float)LogFont->GetPointSize()); return Create(LogFont->GetFace(), Sz, pSurface); } return false; } #endif LFont &LFont::operator =(const LFont &f) { Face(f.Face()); Size(f.Size()); TabSize(f.TabSize()); Quality(f.Quality()); Fore(f.Fore()); Back(f.Back()); SetWeight(f.GetWeight()); Italic(f.Italic()); Underline(f.Underline()); Transparent(f.Transparent()); return *this; } char16 WinSymbolToUnicode[256] = { /* 0 to 15 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16 to 31 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 32 to 47 */ 32, 9998, 9986, 9985, 0, 0, 0, 0, 9742, 9990, 9993, 9993, 0, 0, 0, 0, /* 48 to 63 */ 0, 0, 0, 0, 0, 0, 8987, 9000, 0, 0, 0, 0, 0, 0, 9991, 9997, /* 64 to 79 */ 9997, 9996, 0, 0, 0, 9756, 9758, 9757, 9759, 0, 9786, 9786, 9785, 0, 9760, 0, /* 80 to 95 */ 0, 9992, 9788, 0, 10052, 10014, 10014, 10013, 10016, 10017, 9770, 9775, 2384, 9784, 9800, 9801, /* 96 to 111 */ 9802, 9803, 9804, 9805, 9806, 9807, 9808, 9809, 9810, 9811, 38, 38, 9679, 10061, 9632, 9633, /* 112 to 127 */ 9633, 10065, 10066, 9674, 9674, 9670, 10070, 9670, 8999, 9043, 8984, 10048, 10047, 10077, 10078, 0, /* 128 to 143 */ 9450, 9312, 9313, 9314, 9315, 9316, 9317, 9318, 9319, 9320, 9321, 0, 10102, 10103, 10104, 10105, /* 144 to 159 */ 10106, 10107, 10108, 10109, 10110, 10111, 10087, 9753, 9753, 10087, 10087, 9753, 9753, 10087, 8226, 9679, /* 160 to 175 */ 160, 9675, 9675, 9675, 9737, 9737, 10061, 9642, 9633, 0, 10022, 9733, 10038, 10039, 10040, 10037, /* 176 to 191 */ 0, 0, 10023, 0, 65533, 10026, 10032, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 192 to 207 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10086, 10086, 10086, /* 208 to 223 */ 10086, 10086, 10086, 10086, 10086, 9003, 8998, 0, 10146, 0, 0, 0, 10162, 0, 0, 0, /* 224 to 239 */ 0, 0, 0, 0, 0, 0, 0, 0, 10132, 0, 0, 0, 0, 0, 0, 8678, /* 240 to 255 */ 8680, 8679, 8681, 8660, 8661, 8662, 8663, 8665, 8664, 0, 0, 10007, 10003, 9746, 9745, 0, }; LAutoString LFont::ConvertToUnicode(char16 *Input, ssize_t Len) { LAutoString a; if (LTypeFace::d->IsSymbol) { // F***ing wingdings. if (Input) { LStringPipe p(256); if (Len < 0) Len = StrlenW(Input); char16 *c = Input, *e = Input + Len; while (c < e) { if (*c < 256 && WinSymbolToUnicode[*c]) { p.Write(WinSymbolToUnicode + *c, sizeof(char16)); } else { p.Write(c, sizeof(char16)); } c++; } LAutoWString w(p.NewStrW()); a.Reset(WideToUtf8(w)); } } else { // Normal utf-8 text... a.Reset(WideToUtf8(Input, Len)); } return a; } #if WINNATIVE #include "lgi/common/FontSelect.h" #include "lgi/common/GdiLeak.h" ///////////////////////////////////////////////////////////////////////////// void LFont::_Measure(int &x, int &y, OsChar *Str, int Len) { if (!Handle()) { x = 0; y = 0; return; } HDC hDC = GetSurface() ? GetSurface()->Handle() : CreateCompatibleDC(0); HFONT hOldFont = (HFONT) SelectObject(hDC, Handle()); SIZE Size; if (GetTextExtentPoint32W(hDC, Str, Len, &Size)) { x = Size.cx; y = Size.cy; } else { x = y = 0; } SelectObject(hDC, hOldFont); if (!GetSurface()) DeleteDC(hDC); } int LFont::_CharAt(int x, OsChar *Str, int Len, LPxToIndexType Type) { if (!Handle()) return -1; INT Fit = 0; HDC hDC = CreateCompatibleDC(GetSurface()?GetSurface()->Handle():0); HFONT hOldFont = (HFONT) SelectObject(hDC, Handle()); if (hOldFont) { SIZE Size = {0, 0}; if (!GetTextExtentExPointW(hDC, Str, Len, x, &Fit, 0, &Size)) { DWORD e = GetLastError(); Fit = -1; } else if (Type == LgiNearest && Fit < Len) { // Check if the next char is nearer... SIZE Prev, Next; if (GetTextExtentPoint32W(hDC, Str, Fit, &Prev) && GetTextExtentPoint32W(hDC, Str, Fit + 1, &Next)) { int PrevDist = abs(Prev.cx - x); int NextDist = abs(Next.cx - x); if (NextDist <= PrevDist) Fit++; } } SelectObject(hDC, hOldFont); } else { DWORD e = GetLastError(); Fit = -1; LAssert(0); } DeleteDC(hDC); return Fit; } void LFont::_Draw(LSurface *pDC, int x, int y, OsChar *Str, int Len, LRect *r, LColour &fore) { if (!Handle()) return; HDC hDC = pDC->StartDC(); HFONT hOldFont = (HFONT) SelectObject(hDC, Handle()); if (hOldFont) { bool IsTransparent = Transparent(); SetTextColor(hDC, fore.GetNative()); if (!IsTransparent) SetBkColor(hDC, Back().GetNative()); SetBkMode(hDC, IsTransparent ? TRANSPARENT : OPAQUE); SIZE Size; if ((!IsTransparent && !r) || (GetOwnerUnderline())) { GetTextExtentPoint32W(hDC, Str, Len, &Size); } if (Transparent() && !r) { TextOutW(hDC, x, y, Str, Len); } else { RECT rc; if (r) rc = *r; else { rc.left = x; rc.top = y; rc.right = x + Size.cx; rc.top = y + Size.cy; } /* Debugging stuff... POINT _ori; auto res = GetWindowOrgEx(hDC, &_ori); RECT _rc; int res2 = GetClipBox(hDC, &_rc); auto Addr = (*pDC)[y - _ori.y + 6] + ((x - _ori.x + 4) * pDC->GetBits() / 8); */ ExtTextOutW(hDC, x, y, ETO_CLIPPED | (Transparent()?0:ETO_OPAQUE), &rc, Str, Len, 0); } if (GetOwnerUnderline()) { pDC->Colour(fore); pDC->Line(x, y + GetHeight() - 1, x + Size.cx + 1, y + GetHeight() - 1); } HANDLE h = SelectObject(hDC, hOldFont); } else { DWORD e = GetLastError(); LAssert(0); } pDC->EndDC(); } #elif defined(HAIKU) void LFont::_Measure(int &x, int &y, OsChar *Str, int Len) { printf("%s:%i - _Measure not impl.\n", _FL); } int LFont::_CharAt(int x, OsChar *Str, int Len, LPxToIndexType Type) { if (!d->hFont) return -1; BString s(Str, Len); d->hFont->TruncateString(&s, B_TRUNCATE_BEGINNING, x); return s.CountChars(); } void LFont::_Draw(LSurface *pDC, int x, int y, OsChar *Str, int Len, LRect *r, LColour &fore) { printf("%s:%i - _Draw not impl.\n", _FL); } #else void LFont::_Measure(int &x, int &y, OsChar *Str, int Len) { } int LFont::_CharAt(int x, OsChar *Str, int Len, LPxToIndexType Type) { return -1; } void LFont::_Draw(LSurface *pDC, int x, int y, OsChar *Str, int Len, LRect *r, LColour &fore) { } #endif diff --git a/src/common/General/DateTime.cpp b/src/common/General/DateTime.cpp --- a/src/common/General/DateTime.cpp +++ b/src/common/General/DateTime.cpp @@ -1,2187 +1,2189 @@ /* ** FILE: LDateTime.cpp ** AUTHOR: Matthew Allen ** DATE: 11/11/98 ** DESCRIPTION: Scribe Date Time Object ** ** Copyright (C) 1998, Matthew Allen ** fret@memecode.com */ #define _DEFAULT_SOURCE #include #include #include #include #include #if defined(MAC) #include #endif #ifdef WINDOWS #include #endif #include "lgi/common/Lgi.h" #include "lgi/common/DateTime.h" #include "lgi/common/DocView.h" #if !defined(WINDOWS) constexpr const char *LDateTime::WeekdaysShort[]; constexpr const char *LDateTime::WeekdaysLong[]; constexpr const char *LDateTime::MonthsShort[]; constexpr const char *LDateTime::MonthsLong[]; #define MIN_YEAR 1800 #endif ////////////////////////////////////////////////////////////////////////////// uint16 LDateTime::DefaultFormat = GDTF_DEFAULT; char LDateTime::DefaultSeparator = '/'; uint16 LDateTime::GetDefaultFormat() { if (DefaultFormat == GDTF_DEFAULT) { #ifdef WIN32 TCHAR s[80] = _T("1"); GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDATE, s, CountOf(s)); switch (_tstoi(s)) { case 0: DefaultFormat = GDTF_MONTH_DAY_YEAR; break; default: case 1: DefaultFormat = GDTF_DAY_MONTH_YEAR; break; case 2: DefaultFormat = GDTF_YEAR_MONTH_DAY; break; } GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, s, sizeof(s)); if (_tstoi(s) == 1) { DefaultFormat |= GDTF_24HOUR; } else { DefaultFormat |= GDTF_12HOUR; } if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDATE, s, sizeof(s))) DefaultSeparator = (char)s[0]; if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SSHORTDATE, s, sizeof(s))) { char Sep[] = { DefaultSeparator, '/', '\\', '-', '.', 0 }; LString Str = s; auto t = Str.SplitDelimit(Sep); for (int i=0; i= low && (v) <= high) bool LDateTime::IsValid() const { return InRange(_Day, 1, 31) && InRange(_Year, 1600, 2100) && InRange(_Thousands, 0, 999) && InRange(_Month, 1, 12) && InRange(_Seconds, 0, 59) && InRange(_Minutes, 0, 59) && InRange(_Hours, 0, 23) && InRange(_Tz, -780, 780); } void LDateTime::SetTimeZone(int NewTz, bool ConvertTime) { if (ConvertTime && NewTz != _Tz) { // printf("SetTimeZone: %i\n", NewTz - _Tz); AddMinutes(NewTz - _Tz); } _Tz = NewTz; } int LDateTime::SystemTimeZone(bool ForceUpdate) { if (ForceUpdate || CurTz == NO_ZONE) { CurTz = 0; CurTzOff = 0; #ifdef MAC #ifdef LGI_COCOA NSTimeZone *timeZone = [NSTimeZone localTimeZone]; if (timeZone) { NSDate *Now = [NSDate date]; CurTz = (int) [timeZone secondsFromGMTForDate:Now] / 60; CurTzOff = [timeZone daylightSavingTimeOffsetForDate:Now] / 60; CurTz -= CurTzOff; } #elif defined LGI_CARBON CFTimeZoneRef tz = CFTimeZoneCopySystem(); CFAbsoluteTime now = CFAbsoluteTimeGetCurrent(); Boolean dst = CFTimeZoneIsDaylightSavingTime(tz, now); if (dst) { CFAbsoluteTime next = CFTimeZoneGetNextDaylightSavingTimeTransition(tz, now); CurTz = CFTimeZoneGetSecondsFromGMT(tz, next + 100) / 60; } else { CurTz = CFTimeZoneGetSecondsFromGMT(tz, now) / 60; } CurTzOff = CFTimeZoneGetDaylightSavingTimeOffset(tz, now) / 60; CFRelease(tz); #endif #elif defined(WIN32) timeb tbTime; ftime(&tbTime); CurTz = -tbTime.timezone; TIME_ZONE_INFORMATION Tzi; if (GetTimeZoneInformation(&Tzi) == TIME_ZONE_ID_DAYLIGHT) CurTzOff = -Tzi.DaylightBias; #elif defined(LINUX) || defined(HAIKU) int six_months = (365 * 24 * 60 * 60) / 2; time_t now = 0, then = 0; time (&now); then = now - six_months; tm now_tz, then_tz; tm *t = localtime_r(&now, &now_tz); if (t) { localtime_r(&then, &then_tz); CurTz = now_tz.tm_gmtoff / 60; if (now_tz.tm_isdst) { CurTzOff = (now_tz.tm_gmtoff - then_tz.tm_gmtoff) / 60; CurTz = then_tz.tm_gmtoff / 60; } else // This is not DST so there is no offset right? CurTzOff = 0; // (then_tz.tm_gmtoff - now_tz.tm_gmtoff) / 60; } else return NO_ZONE; #else #error "Impl me." #endif } return CurTz + CurTzOff; } int LDateTime::SystemTimeZoneOffset() { if (CurTz == NO_ZONE) SystemTimeZone(); return CurTzOff; } #if defined WIN32 LDateTime ConvertSysTime(SYSTEMTIME &st, int year) { LDateTime n; if (st.wYear) { n.Year(st.wYear); n.Month(st.wMonth); n.Day(st.wDay); } else { n.Year(year); n.Month(st.wMonth); // Find the 'nth' matching weekday, starting from the first day in the month n.Day(1); LDateTime c = n; for (int i=0; iCompare(b); } #elif defined(LINUX) static bool ParseValue(char *s, LAutoString &var, LAutoString &val) { if (!s) return false; char *e = strchr(s, '='); if (!e) return false; *e++ = 0; var.Reset(NewStr(s)); val.Reset(NewStr(e)); *e = '='; return var != 0 && val != 0; } #endif /* Testing code... LDateTime Start, End; LArray Info; Start.Set("1/1/2010"); End.Set("31/12/2014"); LDateTime::GetDaylightSavingsInfo(Info, Start, &End); LStringPipe p; for (int i=0; i,int> { MonthHash() { for (int i=0; i &Info, LDateTime &Start, LDateTime *End) { bool Status = false; #if defined(WIN32) TIME_ZONE_INFORMATION Tzi; auto r = GetTimeZoneInformation(&Tzi); if (r > TIME_ZONE_ID_UNKNOWN) { Info.Length(0); // Find the dates for the previous year from Start. This allows // us to cover the start of the current year. LDateTime s = ConvertSysTime(Tzi.StandardDate, Start.Year() - 1); LDateTime d = ConvertSysTime(Tzi.DaylightDate, Start.Year() - 1); // Create initial Info entry, as the last change in the previous year auto *i = &Info.New(); if (s < d) { // Year is: Daylight->Standard->Daylight LDateTime tmp = d; i->Offset = -(Tzi.Bias + Tzi.DaylightBias); tmp.AddMinutes(-i->Offset); i->UtcTimeStamp = tmp.Ts(); } else { // Year is: Standard->Daylight->Standard LDateTime tmp = s; i->Offset = -(Tzi.Bias + Tzi.StandardBias); tmp.AddMinutes(-i->Offset); i->UtcTimeStamp = tmp.Ts();; } for (auto y=Start.Year(); y<=(End?End->Year():Start.Year()); y++) { if (s < d) { // Cur year, first event: end of DST i = &Info.New(); auto tmp = ConvertSysTime(Tzi.StandardDate, y); i->Offset = -(Tzi.Bias + Tzi.StandardBias); tmp.AddMinutes(-i->Offset); i->UtcTimeStamp = tmp.Ts(); // Cur year, second event: start of DST i = &Info.New(); tmp = ConvertSysTime(Tzi.DaylightDate, y); i->Offset = -(Tzi.Bias + Tzi.DaylightBias); tmp.AddMinutes(-i->Offset); i->UtcTimeStamp = tmp.Ts(); } else { // Cur year, first event: start of DST i = &Info.New(); auto tmp = ConvertSysTime(Tzi.DaylightDate, Start.Year()); i->Offset = -(Tzi.Bias + Tzi.DaylightBias); tmp.AddMinutes(-i->Offset); i->UtcTimeStamp = tmp.Ts(); // Cur year, second event: end of DST i = &Info.New(); tmp = ConvertSysTime(Tzi.StandardDate, Start.Year()); i->Offset = -(Tzi.Bias + Tzi.StandardBias); tmp.AddMinutes(-i->Offset); i->UtcTimeStamp = tmp.Ts(); } } Status = true; } #elif defined(MAC) LDateTime Before = Start; Before.AddMonths(-6); NSTimeZone *tz = [NSTimeZone systemTimeZone]; NSDate *startDate = [[NSDate alloc] initWithTimeIntervalSince1970:(Before.Ts() / Second64Bit) - Offset1800]; for (int n=0; n<6; n++) { NSDate *next = [tz nextDaylightSavingTimeTransitionAfterDate:startDate]; auto &i = Info.New(); i.UtcTimeStamp = ([next timeIntervalSince1970] + Offset1800) * Second64Bit; i.Offset = (int)([tz secondsFromGMTForDate:[next dateByAddingTimeInterval:60]]/60); #if DEBUG_DST_INFO { LDateTime dt; dt.Set(i.UtcTimeStamp); LgiTrace("%s:%i - Ts=%s Off=%i\n", _FL, dt.Get().Get(), i.Offset); } #endif [startDate release]; startDate = next; } #elif defined(LINUX) if (!Zdump.Length()) { FILE *f = popen("zdump -v /etc/localtime", "r"); if (f) { char s[256]; size_t r; LStringPipe p(1024); while ((r = fread(s, 1, sizeof(s), f)) > 0) { p.Write(s, (int)r); } fclose(f); LString ps = p.NewLStr(); Zdump = ps.Split("\n"); } } MonthHash Lut; LDateTime Prev; int PrevOff = 0; for (auto Line: Zdump) { auto l = Line.SplitDelimit(" \t"); if (l.Length() >= 16 && l[0].Equals("/etc/localtime")) { // /etc/localtime Sat Oct 3 15:59:59 2037 UTC = Sun Oct 4 01:59:59 2037 EST isdst=0 gmtoff=36000 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #if DEBUG_DST_INFO printf("DST: %s\n", Line); #endif LDateTime Utc; Utc.Year(l[5].Int()); auto Tm = l[4].SplitDelimit(":"); if (Tm.Length() != 3) { #if DEBUG_DST_INFO printf("%s:%i - Tm '%s' has wrong parts: %s\n", _FL, l[4], Line); #endif continue; } Utc.Hours(Tm[0].Int()); Utc.Minutes(Tm[1].Int()); Utc.Seconds(Tm[2].Int()); if (Utc.Minutes() < 0) { #if DEBUG_DST_INFO printf("%s:%i - Mins is zero: %s\n", _FL, l[4]); #endif continue; } int m = Lut.Find(l[2]); if (!m) { #if DEBUG_DST_INFO printf("%s:%i - Unknown month '%s'\n", _FL, l[2]); #endif continue; } Utc.Day(l[3].Int()); Utc.Month(m); LAutoString Var, Val; if (!ParseValue(l[14], Var, Val) || stricmp(Var, "isdst")) { #if DEBUG_DST_INFO printf("%s:%i - Unknown value for isdst\n", _FL); #endif continue; } if (!ParseValue(l[15], Var, Val) || stricmp(Var, "gmtoff")) { #if DEBUG_DST_INFO printf("%s:%i - Unknown value for isdst\n", _FL); #endif continue; } int Off = atoi(Val) / 60; if (Utc.Ts() == 0) continue; if (Prev.Year() && Prev < Start && Start < Utc) { // Emit initial entry for 'start' auto &inf = Info.New(); inf.UtcTimeStamp = Prev; inf.Offset = PrevOff; #if DEBUG_DST_INFO printf("Info: Start=%s %i\n", Prev.Get().Get(), inf.Offset); #endif } if (Utc > Start) { // Emit furthur entries for DST events between start and end. auto &inf = Info.New(); inf.UtcTimeStamp = Utc; inf.Offset = Off; #if DEBUG_DST_INFO printf("Info: Next=%s %i\n", Utc.Get().Get(), inf.Offset); #endif if (End && Utc > *End) { // printf("Utc after end: %s > %s\n", Utc.Get().Get(), End->Get().Get()); break; } } Prev = Utc; PrevOff = Off; } } Status = Info.Length() > 1; #else LAssert(!"Not implemented."); #endif return Status; } bool LDateTime::DstToLocal(LArray &Dst, LDateTime &dt) { if (dt.GetTimeZone()) { LAssert(!"Should be a UTC date."); return true; } // LgiTrace("DstToLocal: %s\n", dt.Get().Get()); LAssert(Dst.Length() > 1); // Needs to have at least 2 entries...? for (size_t i=0; i %s\n", (int)i, start.Get().Get(), end.Get().Get()); if (dt >= start && dt < end) { dt.SetTimeZone(a.Offset, true); return true; } } auto Last = Dst.Last(); LDateTime d; d.Set(Last.UtcTimeStamp); if (dt >= d && dt.Year() == d.Year()) { // If it's after the last DST change but in the same year... it's ok... // Just use the last offset. dt.SetTimeZone(Last.Offset, true); return true; } LAssert(!"No valid DST range for this date."); return false; } int LDateTime::DayOfWeek() const { int Index = 0; int Day = IsLeapYear() ? 29 : 28; switch (_Year / 100) { case 19: { Index = 3; break; } case 20: { Index = 2; break; } } // get year right int y = _Year % 100; int r = y % 12; Index = (Index + (y / 12) + r + (r / 4)) % 7; // get month right if (_Month % 2 == 0) { // even month if (_Month > 2) Day = _Month; } else { // odd month switch (_Month) { case 1: { Day = 31; if (IsLeapYear()) { Index = Index > 0 ? Index - 1 : Index + 6; } break; } case 11: case 3: { Day = 7; break; } case 5: { Day = 9; break; } case 7: { Day = 11; break; } case 9: { Day = 5; break; } } } // get day right int Diff = Index - (Day - _Day); while (Diff < 0) Diff += 7; return Diff % 7; } LDateTime LDateTime::Now() { LDateTime dt; dt.SetNow(); return dt; } LDateTime &LDateTime::SetNow() { #ifdef WIN32 SYSTEMTIME stNow; FILETIME ftNow; GetSystemTime(&stNow); SystemTimeToFileTime(&stNow, &ftNow); uint64 i64 = ((uint64)ftNow.dwHighDateTime << 32) | ftNow.dwLowDateTime; Set(i64); #else time_t now; time(&now); struct tm *time = localtime(&now); if (time) *this = time; #ifndef LGI_STATIC else { LgiTrace("%s:%i - Error: localtime failed, now=%u\n", _FL, now); } #endif #endif return *this; } #define Convert24HrTo12Hr(h) ( (h) == 0 ? 12 : (h) > 12 ? (h) % 12 : (h) ) #define Convert24HrToAmPm(h) ( (h) >= 12 ? "p" : "a" ) LString LDateTime::GetDate() const { char s[32]; int Ch = GetDate(s, sizeof(s)); return LString(s, Ch); } int LDateTime::GetDate(char *Str, size_t SLen) const { int Ch = 0; if (Str && SLen > 0) { switch (_Format & GDTF_DATE_MASK) { case GDTF_MONTH_DAY_YEAR: Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_MONTH_LEADINGZ?"%2.2i" :"%i" , _Month); Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_DAY_LEADINGZ ?"%c%2.2i":"%c%i", DefaultSeparator, _Day); Ch += sprintf_s(Str+Ch, SLen-Ch, "%c%i", DefaultSeparator, _Year); break; default: case GDTF_DAY_MONTH_YEAR: Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_DAY_LEADINGZ ?"%2.2i" :"%i" , _Day); Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_MONTH_LEADINGZ?"%c%2.2i":"%c%i", DefaultSeparator, _Month); Ch += sprintf_s(Str+Ch, SLen-Ch, "%c%i", DefaultSeparator, _Year); break; case GDTF_YEAR_MONTH_DAY: Ch += sprintf_s(Str+Ch, SLen-Ch, "%i", _Year); Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_MONTH_LEADINGZ?"%c%2.2i":"%c%i", DefaultSeparator, _Month); Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_DAY_LEADINGZ ?"%c%2.2i":"%c%i", DefaultSeparator, _Day); break; } } return Ch; } LString LDateTime::GetTime() const { char s[32]; int Ch = GetTime(s, sizeof(s)); return LString(s, Ch); } int LDateTime::GetTime(char *Str, size_t SLen) const { int Ch = 0; if (Str && SLen > 0) { switch (_Format & GDTF_TIME_MASK) { case GDTF_12HOUR: default: { Ch += sprintf_s(Str, SLen, "%i:%2.2i:%2.2i%s", Convert24HrTo12Hr(_Hours), _Minutes, _Seconds, Convert24HrToAmPm(_Hours)); break; } case GDTF_24HOUR: { Ch += sprintf_s(Str, SLen, "%i:%2.2i:%2.2i", _Hours, _Minutes, _Seconds); break; } } } return Ch; } uint64 LDateTime::Ts() const { uint64 ts = 0; Get(ts); return ts; } bool LDateTime::SetUnix(uint64 s) { #if defined(WINDOWS) return Set(s * LDateTime::Second64Bit + 116445168000000000LL); #else return Set((s + Offset1800) * LDateTime::Second64Bit); #endif } bool LDateTime::Set(uint64 s) { #if defined WIN32 FILETIME Utc; SYSTEMTIME System; // Adjust to the desired timezone uint64 u = s + ((int64)_Tz * 60 * Second64Bit); Utc.dwHighDateTime = u >> 32; Utc.dwLowDateTime = u & 0xffffffff; if (!FileTimeToSystemTime(&Utc, &System)) return false; _Year = System.wYear; _Month = System.wMonth; _Day = System.wDay; _Hours = System.wHour; _Minutes = System.wMinute; _Seconds = System.wSecond; _Thousands = System.wMilliseconds; return true; #else time_t t = (time_t) (((int64)(s / Second64Bit)) - Offset1800); Set(t); _Thousands = s % Second64Bit; return true; #endif } bool LDateTime::Set(time_t tt) { struct tm *t; #if !defined(_MSC_VER) || _MSC_VER < _MSC_VER_VS2005 if (_Tz) tt += _Tz * 60; t = gmtime(&tt); if (t) #else struct tm tmp; if (_localtime64_s(t = &tmp, &tt) == 0) #endif { _Year = t->tm_year + 1900; _Month = t->tm_mon + 1; _Day = t->tm_mday; _Hours = t->tm_hour; _Minutes = t->tm_min; _Seconds = t->tm_sec; _Thousands = 0; // _Tz = SystemTimeZone(); return true; } return false; } uint64_t LDateTime::OsTime() const { #ifdef WINDOWS FILETIME Utc; SYSTEMTIME System; System.wYear = _Year; System.wMonth = limit(_Month, 1, 12); System.wDay = limit(_Day, 1, 31); System.wHour = limit(_Hours, 0, 23); System.wMinute = limit(_Minutes, 0, 59); System.wSecond = limit(_Seconds, 0, 59); System.wMilliseconds = limit(_Thousands, 0, 999); System.wDayOfWeek = DayOfWeek(); if (SystemTimeToFileTime(&System, &Utc)) { uint64_t s = ((uint64_t)Utc.dwHighDateTime << 32) | Utc.dwLowDateTime; if (_Tz) // Adjust for timezone s -= (int64)_Tz * 60 * Second64Bit; return s; } else { DWORD Err = GetLastError(); LAssert(!"SystemTimeToFileTime failed."); } #else if (_Year < MIN_YEAR) return 0; struct tm t; ZeroObj(t); t.tm_year = _Year - 1900; t.tm_mon = _Month - 1; t.tm_mday = _Day; t.tm_hour = _Hours; t.tm_min = _Minutes; t.tm_sec = _Seconds; t.tm_isdst = -1; time_t sec = timegm(&t); if (sec == -1) return 0; if (_Tz) { // Adjust the output to UTC from the current timezone. sec -= _Tz * 60; } return sec; #endif return 0; } bool LDateTime::OsTime(uint64_t ts) { return Set((time_t)ts); } bool LDateTime::Get(uint64 &s) const { #ifdef WINDOWS if (!IsValid()) { LAssert(!"Needs a valid date."); return false; } s = OsTime(); if (!s) return false; return true; #else if (_Year < MIN_YEAR) return false; auto sec = OsTime(); s = (uint64)(sec + Offset1800) * Second64Bit + _Thousands; return true; #endif } LString LDateTime::Get() const { char buf[32]; int Ch = GetDate(buf, sizeof(buf)); buf[Ch++] = ' '; Ch += GetTime(buf+Ch, sizeof(buf)-Ch); return LString(buf, Ch); } void LDateTime::Get(char *Str, size_t SLen) const { if (Str) { GetDate(Str, SLen); size_t len = strlen(Str); if (len < SLen - 1) { Str[len++] = ' '; GetTime(Str+len, SLen-len); } } } bool LDateTime::Set(const char *Str) { if (!Str) return false; + if (Strlen(Str) > 100) + return false; char Local[256]; strcpy_s(Local, sizeof(Local), Str); char *Sep = strchr(Local, ' '); if (Sep) { *Sep++ = 0; if (!SetTime(Sep)) return false; } if (!SetDate(Local)) return false; return true; } void LDateTime::Month(char *m) { int i = IsMonth(m); if (i >= 0) _Month = i + 1; } int DateComponent(const char *s) { int64 i = Atoi(s); return i ? (int)i : LDateTime::IsMonth(s); } bool LDateTime::SetDate(const char *Str) { bool Status = false; if (Str) { auto T = LString(Str).SplitDelimit("/-.,_\\"); if (T.Length() == 3) { int i[3] = { DateComponent(T[0]), DateComponent(T[1]), DateComponent(T[2]) }; int fmt = _Format & GDTF_DATE_MASK; // Do some guessing / overrides. // Don't let _Format define the format completely. if (i[0] > 1000) { fmt = GDTF_YEAR_MONTH_DAY; } else if (i[2] > 1000) { if (i[0] > 12) fmt = GDTF_DAY_MONTH_YEAR; else if (i[1] > 12) fmt = GDTF_MONTH_DAY_YEAR; } switch (fmt) { case GDTF_MONTH_DAY_YEAR: { _Month = i[0]; _Day = i[1]; _Year = i[2]; break; } case GDTF_DAY_MONTH_YEAR: { _Day = i[0]; _Month = i[1]; _Year = i[2]; break; } case GDTF_YEAR_MONTH_DAY: { _Year = i[0]; _Month = i[1]; _Day = i[2]; break; } default: { _Year = i[2]; if ((DefaultFormat & GDTF_DATE_MASK) == GDTF_MONTH_DAY_YEAR) { // Assume m/d/yyyy _Day = i[1]; _Month = i[0]; } else { // Who knows??? // Assume d/m/yyyy _Day = i[0]; _Month = i[1]; } break; } } if (_Year < 100) { LAssert(_Day < 1000 && _Month < 1000); if (_Year >= 80) _Year += 1900; else _Year += 2000; } Status = true; } else { // Fall back to fuzzy matching auto T = LString(Str).SplitDelimit(" ,"); MonthHash Lut; int FMonth = 0; int FDay = 0; int FYear = 0; for (unsigned i=0; i 0) { if (i >= 1000) { FYear = i; } else if (i < 32) { FDay = i; } } } else { int i = Lut.Find(p); if (i) FMonth = i; } } if (FMonth && FDay) { Day(FDay); Month(FMonth); } if (FYear) { Year(FYear); } else { LDateTime Now; Now.SetNow(); Year(Now.Year()); } } } return Status; } bool LDateTime::SetTime(const char *Str) { if (!Str) return false; auto T = LString(Str).SplitDelimit(":."); if (T.Length() < 2 || T.Length() > 4) return false; _Hours = (int)T[0].Int(); _Minutes = (int)T[1].Int(); if (_Hours < 0 || _Minutes < 0) return false; char *s = T[2]; if (s) _Seconds = atoi(s); else _Seconds = 0; _Thousands = 0; s = T.Last(); if (s) { if (strchr(s, 'p') || strchr(s, 'P')) { if (_Hours != 12) _Hours += 12; } else if (strchr(s, 'a') || strchr(s, 'A')) { if (_Hours == 12) _Hours -= 12; } } if (T.Length() > 3) { LString t = "0."; t += s; _Thousands = (int) (t.Float() * 1000); } return true; } int LDateTime::IsWeekDay(const char *s) { for (unsigned n=0; n= 4) { Year((int)t[0].Int()); Month((int)t[1].Int()); Day((int)t[2].Int()); } else if (t[2].Length() >= 4) { Day((int)t[0].Int()); Month((int)t[1].Int()); Year((int)t[2].Int()); } else { LAssert(!"Unknown date format?"); return false; } } } else if (a[i].Length() == 4) Year((int)a[i].Int()); else if (!Day()) Day((int)a[i].Int()); } else if (IsAlpha(*c)) { int WkDay = IsWeekDay(c); if (WkDay >= 0) continue; int Mnth = IsMonth(c); if (Mnth >= 0) Month(Mnth + 1); } else if (*c == '-' || *c == '+') { c++; if (strlen(c) == 4) { // Timezone.. int64 Tz = a[i].Int(); int Hrs = (int) (Tz / 100); int Min = (int) (Tz % 100); SetTimeZone(Hrs * 60 + Min, false); } } } return IsValid(); } int LDateTime::Sizeof() { return sizeof(int) * 7; } bool LDateTime::Serialize(LFile &f, bool Write) { int32 i; if (Write) { #define wf(fld) i = fld; f << i; wf(_Day); wf(_Month); wf(_Year); wf(_Thousands); wf(_Seconds); wf(_Minutes); wf(_Hours); } else { #define rf(fld) f >> i; fld = i; rf(_Day); rf(_Month); rf(_Year); rf(_Thousands); rf(_Seconds); rf(_Minutes); rf(_Hours); } return true; } /* bool LDateTime::Serialize(ObjProperties *Props, char *Name, bool Write) { #ifndef LGI_STATIC if (Props && Name) { struct _Date { uint8_t Day; uint8_t Month; int16_t Year; uint8_t Hour; uint8_t Minute; uint16_t ThouSec; }; LAssert(sizeof(_Date) == 8); if (Write) { _Date d; d.Day = _Day; d.Month = _Month; d.Year = _Year; d.Hour = _Hours; d.Minute = _Minutes; d.ThouSec = (_Seconds * 1000) + _Thousands; return Props->Set(Name, &d, sizeof(d)); } else // Read { void *Ptr; int Len; if (Props->Get(Name, Ptr, Len) && sizeof(_Date) == Len) { _Date *d = (_Date*) Ptr; _Day = d->Day; _Month = d->Month; _Year = d->Year; _Hours = d->Hour; _Minutes = d->Minute; _Seconds = d->ThouSec / 1000; _Thousands = d->ThouSec % 1000; return true; } } } #endif return false; } */ int LDateTime::Compare(const LDateTime *Date) const { // this - *Date auto ThisTs = IsValid() ? Ts() : 0; auto DateTs = Date->IsValid() ? Date->Ts() : 0; // If these ever fire, the cast to int64_t will overflow LAssert((ThisTs & 0x800000000000000) == 0); LAssert((DateTs & 0x800000000000000) == 0); int64_t Diff = (int64_t)ThisTs - DateTs; if (Diff < 0) return -1; return Diff > 0 ? 1 : 0; } #define DATETIME_OP(op) \ bool LDateTime::operator op(const LDateTime &dt) const \ { \ auto a = Ts(); \ auto b = dt.Ts(); \ return a op b; \ } DATETIME_OP(<) DATETIME_OP(<=) DATETIME_OP(>) DATETIME_OP(>=) bool LDateTime::operator ==(const LDateTime &dt) const { return _Year == dt._Year && _Month == dt._Month && _Day == dt._Day && _Hours == dt._Hours && _Minutes == dt._Minutes && _Seconds == dt._Seconds && _Thousands == dt._Thousands; } bool LDateTime::operator !=(const LDateTime &dt) const { return _Year != dt._Year || _Month != dt._Month || _Day != dt._Day || _Hours != dt._Hours || _Minutes != dt._Minutes || _Seconds != dt._Seconds || _Thousands != dt._Thousands; } int LDateTime::DiffMonths(const LDateTime &dt) { int a = (Year() * 12) + Month(); int b = (dt.Year() * 12) + dt.Month(); return b - a; } LDateTime LDateTime::operator -(const LDateTime &dt) { uint64 a, b; Get(a); dt.Get(b); /// Resolution of a second when using 64 bit timestamps int64 Sec = Second64Bit; int64 Min = 60 * Sec; int64 Hr = 60 * Min; int64 Day = 24 * Hr; int64 d = (int64)a - (int64)b; LDateTime r; r._Day = (int16) (d / Day); d -= r._Day * Day; r._Hours = (int16) (d / Hr); d -= r._Hours * Hr; r._Minutes = (int16) (d / Min); d -= r._Minutes * Min; r._Seconds = (int16) (d / Sec); #ifdef WIN32 d -= r._Seconds * Sec; r._Thousands = (int16) (d / 10000); #else r._Thousands = 0; #endif return r; } LDateTime LDateTime::operator +(const LDateTime &dt) { LDateTime s = *this; s.AddMonths(dt.Month()); s.AddDays(dt.Day()); s.AddHours(dt.Hours()); s.AddMinutes(dt.Minutes()); // s.AddSeconds(dt.Seconds()); return s; } LDateTime &LDateTime::operator =(const LDateTime &t) { _Day = t._Day; _Year = t._Year; _Thousands = t._Thousands; _Month = t._Month; _Seconds = t._Seconds; _Minutes = t._Minutes; _Hours = t._Hours; _Tz = t._Tz; _Format = t._Format; return *this; } LDateTime &LDateTime::operator =(struct tm *time) { if (time) { _Seconds = time->tm_sec; _Minutes = time->tm_min; _Hours = time->tm_hour; _Day = time->tm_mday; _Month = time->tm_mon + 1; _Year = time->tm_year + 1900; } else Empty(); return *this; } bool LDateTime::IsSameDay(LDateTime &d) const { return Day() == d.Day() && Month() == d.Month() && Year() == d.Year(); } bool LDateTime::IsSameMonth(LDateTime &d) const { return Day() == d.Day() && Month() == d.Month(); } bool LDateTime::IsSameYear(LDateTime &d) const { return Year() == d.Year(); } bool LDateTime::IsLeapYear(int Year) const { if (Year < 0) Year = _Year; if (Year % 4 != 0) { return false; } if (Year % 400 == 0) { return true; } if (Year % 100 == 0) { return false; } return true; } int LDateTime::DaysInMonth() const { if (_Month == 2 && IsLeapYear()) { return 29; } short DaysInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; return _Month >= 1 && _Month <= 12 ? DaysInMonth[_Month-1] : 0; } #define MinutesInDay (60*24) void LDateTime::AddSeconds(int64 Seconds) { uint64 i; if (Get(i)) { i += Seconds * Second64Bit; Set(i); } } void LDateTime::AddMinutes(int64 Minutes) { uint64 i; if (Get(i)) { int64 delta = Minutes * 60 * Second64Bit; uint64 n = i + delta; // printf("AddMin " LPrintfInt64 " + " LPrintfInt64 " = " LPrintfInt64 "\n", i, delta, n); Set(n); #if 0 uint64 i2; Get(i2); int64 diff = (int64)i2-(int64)i; #endif } } void LDateTime::AddHours(int64 Hours) { uint64 i; if (Get(i)) { i += Hours * LDateTime::HourLength * Second64Bit; Set(i); } } bool LDateTime::AddDays(int64 Days) { if (!Days) return true; uint64 Ts; if (!Get(Ts)) return false; Ts += Days * LDateTime::DayLength * Second64Bit; bool b = Set(Ts); return b; } void LDateTime::AddMonths(int64 Months) { int64 m = _Month + Months; do { if (m < 1) { _Year--; m += 12; } else if (m > 12) { _Year++; m -= 12; } else { break; } } while (1); _Month = (int16) m; if (_Day > DaysInMonth()) _Day = DaysInMonth(); } LString LDateTime::DescribePeriod(double seconds) { int mins = (int) (seconds / 60); seconds -= mins * 60; int hrs = mins / 60; mins -= hrs * 60; int days = hrs / 24; hrs -= days * 24; LString s; if (days > 0) s.Printf("%id %ih %im %is", days, hrs, mins, (int)seconds); else if (hrs > 0) s.Printf("%ih %im %is", hrs, mins, (int)seconds); else if (mins > 0) s.Printf("%im %is", mins, (int)seconds); else s.Printf("%is", (int)seconds); return s; } LString LDateTime::DescribePeriod(LDateTime to) { auto ThisTs = Ts(); auto ToTs = to.Ts(); auto diff = ThisTs < ToTs ? ToTs - ThisTs : ThisTs - ToTs; auto seconds = (double)diff / LDateTime::Second64Bit; return DescribePeriod(seconds); } int LDateTime::MonthFromName(const char *Name) { if (Name) { for (int m=0; m<12; m++) { if (strnicmp(Name, MonthsShort[m], strlen(MonthsShort[m])) == 0) { return m + 1; break; } } } return -1; } bool LDateTime::Decode(const char *In) { // Test data: // // Tue, 6 Dec 2005 1:25:32 -0800 Empty(); if (!In) { LAssert(0); return false; } bool Status = false; // Tokenize delimited by whitespace LString::Array T = LString(In).SplitDelimit(", \t\r\n"); if (T.Length() < 2) { if (T[0].IsNumeric()) { // Some sort of timestamp? uint64_t Ts = Atoi(T[0].Get()); if (Ts > 0) { return SetUnix(Ts); } else return false; } else { // What now? return false; } } else { bool GotDate = false; for (unsigned i=0; i 31) { // Y/M/D? Year((int)Date[0].Int()); Day((int)Date[2].Int()); } else if (Date[2].Int() > 31) { // D/M/Y? Day((int)Date[0].Int()); Year((int)Date[2].Int()); } else { // Ambiguous year... bool YrFirst = true; if (Date[0].Length() == 1) YrFirst = false; // else we really can't tell.. just go with year first if (YrFirst) { Year((int)Date[0].Int()); Day((int)Date[2].Int()); } else { Day((int)Date[0].Int()); Year((int)Date[2].Int()); } LDateTime Now; Now.SetNow(); if (Year() + 2000 <= Now.Year()) Year(2000 + Year()); else Year(1900 + Year()); } if (Date[1].IsNumeric()) Month((int)Date[1].Int()); else { int m = MonthFromName(Date[1]); if (m > 0) Month(m); } GotDate = true; Status = true; } else if (s.Find(":") >= 0) { // whole time // Do some validation bool Valid = true; for (char *c = s; *c && Valid; c++) { if (!(IsDigit(*c) || *c == ':')) Valid = false; } if (Valid) { LString::Array Time = s.Split(":"); if (Time.Length() == 2 || Time.Length() == 3) { // Hour int i = (int) Time[0].Int(); if (i >= 0) Hours(i); if (s.Lower().Find("p") >= 0) { if (Hours() < 12) Hours(Hours() + 12); } // Minute i = (int) Time[1].Int(); if (i >= 0) Minutes(i); if (Time.Length() == 3) { // Second i = (int) Time[2].Int(); if (i >= 0) Seconds(i); } Status = true; } } } else if (IsAlpha(s(0))) { // text int m = MonthFromName(s); if (m > 0) Month(m); } else if (strchr("+-", *s)) { // timezone DoTimeZone: LDateTime Now; double OurTmz = (double)Now.SystemTimeZone() / 60; if (s && strchr("-+", *s) && strlen(s) == 5) { #if 1 int i = atoi(s); int hr = i / 100; int min = i % 100; SetTimeZone(hr * 60 + min, false); #else // adjust for timezone char Buf[32]; memcpy(Buf, s, 3); Buf[3] = 0; double TheirTmz = atof(Buf); memcpy(Buf+1, s + 3, 2); TheirTmz += (atof(Buf) / 60); if (Tz) { *Tz = TheirTmz; } double AdjustHours = OurTmz - TheirTmz; AddMinutes((int) (AdjustHours * 60)); #endif } else { // assume GMT AddMinutes((int) (OurTmz * 60)); } } else if (s.IsNumeric()) { int Count = 0; for (char *c = s; *c; c++) { if (!IsDigit(*c)) break; Count++; } if (Count <= 2) { if (Day()) { // We already have a day... so this might be // a 2 digit year... LDateTime Now; Now.SetNow(); int Yr = atoi(s); if (2000 + Yr <= Now.Year()) Year(2000 + Yr); else Year(1900 + Yr); } else { // A day number (hopefully)? Day((int)s.Int()); } } else if (Count == 4) { if (!Year()) { // A year! Year((int)s.Int()); Status = true; } else { goto DoTimeZone; } // My one and only Y2K fix // d.Year((Yr < 100) ? (Yr > 50) ? 1900+Yr : 2000+Yr : Yr); } } } } return Status; } bool LDateTime::GetVariant(const char *Name, LVariant &Dst, char *Array) { LDomProperty p = LStringToDomProp(Name); switch (p) { case DateYear: // Type: Int32 Dst = Year(); break; case DateMonth: // Type: Int32 Dst = Month(); break; case DateDay: // Type: Int32 Dst = Day(); break; case DateHour: // Type: Int32 Dst = Hours(); break; case DateMinute: // Type: Int32 Dst = Minutes(); break; case DateSecond: // Type: Int32 Dst = Seconds(); break; case DateDate: // Type: String { char s[32]; GetDate(s, sizeof(s)); Dst = s; break; } case DateTime: // Type: String { char s[32]; GetTime(s, sizeof(s)); Dst = s; break; } case TypeString: // Type: String case DateDateAndTime: // Type: String { char s[32]; Get(s, sizeof(s)); Dst = s; break; } case TypeInt: // Type: Int64 case DateTimestamp: // Type: Int64 { uint64 i = 0; Get(i); Dst = (int64)i; break; } case DateSecond64Bit: { Dst = Second64Bit; break; } default: { return false; } } return true; } bool LDateTime::SetVariant(const char *Name, LVariant &Value, char *Array) { LDomProperty p = LStringToDomProp(Name); switch (p) { case DateYear: Year(Value.CastInt32()); break; case DateMonth: Month(Value.CastInt32()); break; case DateDay: Day(Value.CastInt32()); break; case DateHour: Hours(Value.CastInt32()); break; case DateMinute: Minutes(Value.CastInt32()); break; case DateSecond: Seconds(Value.CastInt32()); break; case DateDate: SetDate(Value.Str()); break; case DateTime: SetTime(Value.Str()); break; case DateDateAndTime: Set(Value.Str()); break; case DateTimestamp: Set((uint64)Value.CastInt64()); break; default: return false; } return true; } bool LDateTime::CallMethod(const char *Name, LVariant *ReturnValue, LArray &Args) { switch (LStringToDomProp(Name)) { case DateSetNow: SetNow(); if (ReturnValue) *ReturnValue = true; break; case DateSetStr: if (Args.Length() < 1) return false; bool Status; if (Args[0]->Type == GV_INT64) Status = Set((uint64) Args[0]->Value.Int64); else Status = Set(Args[0]->Str()); if (ReturnValue) *ReturnValue = Status; break; case DateGetStr: { char s[256] = ""; Get(s, sizeof(s)); if (ReturnValue) *ReturnValue = s; break; } default: return false; } return true; } #ifdef _DEBUG #define DATE_ASSERT(i) \ if (!(i)) \ { \ LAssert(!"LDateTime unit test failed."); \ return false; \ } bool LDateTime_Test() { // Check 64bit get/set LDateTime t("1/1/2017 0:0:0"); uint64 i; DATE_ASSERT(t.Get(i)); LgiTrace("Get='%s'\n", t.Get().Get()); uint64 i2 = i + (24ULL * 60 * 60 * LDateTime::Second64Bit); LDateTime t2; t2.SetFormat(GDTF_DAY_MONTH_YEAR); t2.Set(i2); LString s = t2.Get(); LgiTrace("Set='%s'\n", s.Get()); DATE_ASSERT(!stricmp(s, "2/1/2017 12:00:00a") || !stricmp(s, "2/01/2017 12:00:00a")); t.SetNow(); LgiTrace("Now.Local=%s Tz=%.2f\n", t.Get().Get(), t.GetTimeZoneHours()); t2 = t; t2.ToUtc(); LgiTrace("Now.Utc=%s Tz=%.2f\n", t2.Get().Get(), t2.GetTimeZoneHours()); t2.ToLocal(); LgiTrace("Now.Local=%s Tz=%.2f\n", t2.Get().Get(), t2.GetTimeZoneHours()); DATE_ASSERT(t == t2); return true; } #endif