Problem Overview
WinCC C-Script projects frequently require the runtime to write the combined date and time picked from a Calendar Control 11.0 ActiveX into a Date/Time internal tag. The Calendar Control exposes only the date component through its Value property, so engineers must concatenate a fixed or computed time string and pass the resulting buffer to a Date/Time tag via the SetTagChar API. Three recurring symptoms appear in the field:
- The tag updates with the selected date but the time component always shows 12:00:00 in the HMI output.
- A custom time string such as "23:59:59" writes correctly, but "00:00:00" reverts to 12:00:00 immediately after the
SetTagCharcall returns. - The Calendar Control
Valueproperty returns the date in a regional format (DD.MM.YYYY,MM/DD/YYYY, orYYYY-MM-DD) that the WinCC Date/Time tag parser cannot interpret directly, producing tag write failures or default values.
This reference documents the underlying WinCC Date/Time tag format, the C-Script tag I/O APIs involved, a production-ready string concatenation pattern, and the specific workarounds required to write a literal 00:00:00 time. The patterns apply to SIMATIC WinCC V7.0 through V7.5 SP2 and WinCC Professional V13 SP1 through V18 (TIA Portal) C-Script editors that ship the legacy Microsoft Calendar Control 11.0 (MSCAL.OCX) wrapper.
Reference documentation: Siemens Industry Online Support for the SIMATIC WinCC V7.5 manual set, the WinCC Professional scripting manual, and the WinCC option documentation.
Prerequisites and Environment
Confirm the following before adapting the code to a target project:
- WinCC V7.4 SP1, V7.5, V7.5 SP1, V7.5 SP2, or WinCC Professional V15.1 through V18 in TIA Portal with the C-Script runtime option licensed.
- Microsoft Calendar Control 11.0 (MSCAL.OCX) registered on the development PC. Verify with
regeditunderHKEY_CLASSES_ROOT\TypeLib\{8E27C92B-1264-101C-8A2F-040224009C02}on Windows 7 / Windows 10 / Windows 11 64-bit systems. The CLSID must resolve tomscal.ocx. - A Date/Time internal tag (for example
BeginDate) defined in WinCC Tag Management with the type "Date/Time" and a valid connection assignment. - An ActiveX wrapper inserted on the target picture with the object name matching the C-Script reference (commonly
CalendarBeginDate). - A project function or local C-Action attached to the Calendar's
ValueChangedorClickevent. - Local Administrator rights on the engineering station so that MSCAL.OCX can be re-registered if the Calendar wrapper fails to instantiate.
Cross-reference: Siemens Industry Online Support for the SIMATIC HMI option documentation, WinCC scripting manuals, and the TIA Portal WinCC Professional system manual.
Calendar Control 11.0 ActiveX Properties
The Microsoft Calendar Control 11.0 (programmatic identifier MSComCtl2.Calendar.2 or, in some legacy builds, MSCAL.Calendar.7) exposes several properties accessible from WinCC C-Script via the GetPropChar / SetPropChar wrappers. The properties relevant to date/time tag writes are:
| Property | Type | Description |
|---|---|---|
| Value | VARIANT / char* | Returns the currently selected date as a string formatted per the regional locale of the engineering station. The time component is always 00:00:00 at the COM layer regardless of locale. |
| Day | short (Word) | Numeric day-of-month, 1-31. Use GetPropWord. |
| Month | short (Word) | Numeric month, 1-12. Use GetPropWord. |
| Year | short (Word) | Four-digit year. Use GetPropWord. |
| FirstDay | long | Index of the first day of the week (0 = system default). |
| ShowDateSelectors | BOOL | True if the spin-button selectors are visible above the calendar grid. |
| ShowDays | BOOL | True if the day-of-week header row is visible. |
| ShowHorizontalGrid | BOOL | True if the horizontal grid lines are drawn. |
| ShowTitle | BOOL | True if the month/year title bar is visible. |
| TitleFont | IFontDisp | Font used in the calendar title bar. |
| DayFont | IFontDisp | Font used for day-of-week and day-of-month numbers. |
The Value property is the only property that returns a complete date string, and its format depends on the regional settings of the engineering station's Windows installation. Typical outputs include:
- US English locale:
MM/DD/YYYY(e.g.,11/25/2024) - German locale:
DD.MM.YYYY(e.g.,25.11.2024) - ISO 8601 locale (UK English or Swedish):
YYYY-MM-DD(e.g.,2024-11-25) - French locale:
DD/MM/YYYY(e.g.,25/11/2024)
WinCC's Date/Time tag parser expects the literal format YYYY-MM-DD HH:MM:SS in both V7.x and WinCC Professional. A regional date string without explicit ISO 8601 reformatting is rejected or misinterpreted, which is why direct passing of the Calendar Value to SetTagChar fails in non-English locales.
WinCC C-Script Tag I/O Functions
WinCC C-Script exposes a flat C API for property and tag access. The relevant calls for this use case are documented in apdefap.h, which ships with the WinCC installation under WinCC\aplib\:
| Function | Signature | Purpose |
|---|---|---|
| GetPropChar | char* GetPropChar(LPCTSTR lpszPictureName, LPCTSTR lpszObjectName, LPCTSTR lpszPropertyName) |
Read a string property from a graphic object. Returns a pointer to an internal buffer that remains valid until the next GetPropChar call on the same thread. |
| SetPropChar | BOOL SetPropChar(LPCTSTR lpszPictureName, LPCTSTR lpszObjectName, LPCTSTR lpszPropertyName, LPCTSTR lpszValue) |
Write a string property to a graphic object. Returns nonzero on success. |
| GetPropWord | WORD GetPropWord(LPCTSTR lpszPictureName, LPCTSTR lpszObjectName, LPCTSTR lpszPropertyName) |
Read a numeric property as a 16-bit unsigned value. |
| SetTagChar | BOOL SetTagChar(LPCTSTR lpszTagName, LPCTSTR lpszValue) |
Write a string value to an internal tag. WinCC performs the string-to-Date/Time conversion internally. |
| GetTagChar | char* GetTagChar(LPCTSTR lpszTagName) |
Read a Date/Time tag as a formatted ISO 8601 string. Returns a transient pointer. |
| SetTagWord | BOOL SetTagWord(LPCTSTR lpszTagName, WORD wValue) |
Write a 16-bit unsigned value. |
| SetTagFloat | BOOL SetTagFloat(LPCTSTR lpszTagName, double dValue) |
Write a 64-bit double-precision value. |
The Calendar Control's Value property can be retrieved with GetPropChar(lpszPictureName, "CalendarBeginDate", "Value"). The function returns a pointer to an internal WinCC buffer; the buffer is reused by subsequent GetPropChar calls in the same thread, so copy the result into a project-owned char array before any other API call executes.
CS0008: declaration after statement.
Date/Time Tag Internal Format
WinCC stores Date/Time tags as a 64-bit Windows FILETIME value representing the number of 100-nanosecond intervals since 1601-01-01 00:00:00 UTC. The SetTagChar wrapper converts the string argument to FILETIME using a locale-independent parser that recognizes the exact format YYYY-MM-DD HH:MM:SS with literal hyphen, space, and colon separators. Any deviation from this exact format causes one of three failures:
- The tag is written but the time component reverts to 12:00:00 because the parser interprets the unrecognized string as a date-only value and substitutes the canonical noon default.
- The
SetTagCharcall returnsFALSE(zero) and the tag retains its previous value. No event is raised; the failure is silent unless the script logs it explicitly. - The tag displays garbage characters in the HMI I/O field because the FILETIME conversion produced an out-of-range value or an invalid UTF-16 surrogate pair in the OPC UA layer.
This is the root cause of the 12:00:00 default. When the Calendar Control returns 11/25/2024 (US locale) and the script concatenates 00:00:00, the resulting string 11/25/2024 00:00:00 is not a valid WinCC Date/Time string. The parser falls back to date-only interpretation and uses 12:00:00 noon as the canonical default. The same mechanism explains why "23:59:59" appears to work in some configurations: if the trailing :59 happens to align with a valid alternative parser path on the runtime machine, the tag is accepted with the literal value; otherwise the same fallback applies.
The string-to-FILETIME parser in WinCC V7.x is implemented in CCAggrTlg.dll and in HMIRTM.exe on WinCC Professional. The parser does not call strptime or VariantChangeType; it uses a hand-written state machine that expects the fixed width pattern. Localized date strings are rejected unless the calling C-Script pre-processes them into ISO 8601.
Concatenating Date and Time Strings
To produce a valid WinCC Date/Time string, the script must reformat the Calendar output to ISO 8601 and append the time literal. The canonical pattern is:
char* dia;
char datebuf[32];
char datetime[40];
int m, d, y;
dia = GetPropChar(lpszPictureName, "CalendarBeginDate", "Value");
if (dia == NULL) return 1;
/* Parse US-locale MM/DD/YYYY. Adjust sscanf format for other locales. */
if (sscanf(dia, "%d/%d/%d", &m, &d, &y) != 3) return 1;
sprintf(datebuf, "%04d-%02d-%02d", y, m, d);
sprintf(datetime, "%s 23:59:59", datebuf);
SetTagChar("BeginDate", datetime);
This pattern handles the locale issue at the source by parsing three integers and rebuilding the string in ISO 8601. The fixed time 23:59:59 is concatenated with a single space separator. The %04d width specifier guarantees a four-digit year, which is required by the parser for years before 1000 AD.
For German locale where the Calendar returns 25.11.2024, replace the sscanf line with:
if (sscanf(dia, "%d.%d.%d", &d, &m, &y) != 3) return 1;
For French locale (25/11/2024):
if (sscanf(dia, "%d/%d/%d", &d, &m, &y) != 3) return 1;
For projects that must handle multiple locales on a single runtime, dispatch on the separator character detected in the dia buffer:
char sep = dia[2];
if (sep == '-') n = sscanf(dia, "%d-%d-%d", &y, &m, &d);
else if (sep == '/') n = sscanf(dia, "%d/%d/%d", &m, &d, &y);
else if (sep == '.') n = sscanf(dia, "%d.%d.%d", &d, &m, &y);
else return 1;
The separator character is always at offset 2 for the supported regional formats because the day and month components are two-digit zero-padded strings.
Step-by-Step Implementation
The full implementation follows these steps:
- Open the WinCC picture that contains the Calendar ActiveX wrapper. Confirm the object name matches the property name string passed to
GetPropChar. The default object name in WinCC V7.x isControl1; rename it toCalendarBeginDatevia the object's Properties dialog for clarity. - Open the picture's events list (right-click the object > Properties > Events) and locate the Calendar's
ValueChangedorClickevent. Right-click the event row and select "C-Action" to attach a C-Script. - Paste the canonical C-Script into the editor. The script must declare all local variables at the top of the function block because WinCC's C-Script editor is C89-strict.
- Compile the picture with F7 or via Graphics Designer > Compile. Resolve any "implicit declaration" warnings by adding missing prototypes; the most common omission is
int sscanf(const char*, const char*, ...). - Save the picture and start the runtime. Click several dates in the Calendar and observe the configured diagnostic tag.
- Open the WinCC Tag Simulator (Start > Programs > Siemens Automation > SIMATIC > WinCC > Tools > WinCC Tag Simulator) and verify that
BeginDateupdates with the selected date and the configured time. - Confirm the value round-trips by reading the tag back with
GetTagChar("BeginDate")in a second C-Action on a button press, then logging the result to a String tag.
A production-ready version with locale auto-detection and explicit error handling:
#include "apdefap.h"
void OnCalendarChange(char* lpszPictureName, char* lpszObjectName, char* lpszPropertyName)
{
char* dia;
char datebuf[32];
char datetime[40];
int m = 0, d = 0, y = 0;
int n = 0;
char sep;
dia = GetPropChar(lpszPictureName, "CalendarBeginDate", "Value");
if (dia == NULL) {
SetTagWord("DiagCalendarWriteError", 1);
return;
}
sep = dia[2];
if (sep == '-') {
n = sscanf(dia, "%d-%d-%d", &y, &m, &d);
} else if (sep == '/') {
n = sscanf(dia, "%d/%d/%d", &m, &d, &y);
} else if (sep == '.') {
n = sscanf(dia, "%d.%d.%d", &d, &m, &y);
}
if (n != 3) {
SetTagWord("DiagCalendarWriteError", 2);
return;
}
sprintf(datebuf, "%04d-%02d-%02d", y, m, d);
sprintf(datetime, "%s 23:59:59", datebuf);
if (!SetTagChar("BeginDate", datetime)) {
SetTagWord("DiagCalendarWriteError", 3);
} else {
SetTagWord("DiagCalendarWriteError", 0);
}
}
The apdefap.h include is mandatory because it declares the WinCC C runtime prototypes. Without it, the compiler emits "implicit function declaration" errors that corrupt the build. The diagnostic tag DiagCalendarWriteError exposes a numeric status code that maps to 1 = NULL pointer from GetPropChar, 2 = sscanf parse failure, 3 = SetTagChar returned FALSE, and 0 = success.
Handling the 00:00:00 Edge Case
When the time literal changes from 23:59:59 to 00:00:00, the visible tag may still display 12:00:00 because of three distinct issues that interact with each other.
Issue 1 - Leading-zero truncation in the source. If the project was migrated from an older WinCC version (V6.x or earlier), the C-Script source may contain a stray carriage return, line feed, or UTF-8 BOM before the time string literal. Use sprintf to guarantee the exact format:
sprintf(datetime, "%s 00:00:00", datebuf);
Do not concatenate string literals across multiple C statements; some C-Script compilers in WinCC V7.0 SP3 and earlier concatenate adjacent string literals with an inserted space, breaking the format. The single-call sprintf form is portable across all WinCC versions.
Issue 2 - Tag display format. The Date/Time tag is stored correctly as a FILETIME but the I/O field displaying the tag is configured with a custom format string. Open the I/O field properties, switch to the "Output/Input" tab, and confirm the Output format is set to a string that includes "HH:mm:ss". Common format codes:
| Format Code | Display Result | Use Case |
|---|---|---|
| yyyy-MM-dd HH:mm:ss | 2024-11-25 00:00:00 | ISO 8601 logs and OPC UA export |
| dd.MM.yyyy HH:mm:ss | 25.11.2024 00:00:00 | German locale HMI |
| MM/dd/yyyy hh:mm:ss tt | 11/25/2024 12:00:00 AM | US locale with AM/PM |
| HH:mm:ss | 00:00:00 | Time-of-day only display |
| yyyy-MM-dd | 2024-11-25 | Date only; time defaults to 12:00:00 visually |
If the I/O field format omits "HH:mm:ss", the displayed value falls back to 12:00:00 even when the underlying tag FILETIME is exactly midnight. This is the most common cause of the 00:00:00 not appearing in the HMI. Verify by selecting the I/O field and pressing F8 to open the format editor.
Issue 3 - Script re-entry. The Calendar Control fires ValueChanged on each date click. If the script writes the tag from multiple places - for example, both the Calendar event and a separate reset button - an earlier handler may overwrite the 00:00:00 with 12:00:00. Add an internal tag CalendarWriteOwner and gate the write:
if (GetTagWord("CalendarWriteOwner") == 1) return;
SetTagChar("BeginDate", datetime);
Issue 4 - 12-hour vs 24-hour conversion. On runtime stations with US locale and 12-hour clock, the parser may interpret "00:00:00" as a 12-hour-format string (12:00 AM) and display it as "12:00:00 AM". Force 24-hour format by using uppercase HH instead of lowercase hh in the I/O field Output format. Lowercase hh is interpreted as 1-12 with AM/PM suffix; uppercase HH is interpreted as 00-23 without suffix.
Issue 5 - Tag type mismatch. Confirm the internal tag is "Date/Time" and not "String" or "Text tag". A String tag will store the literal text "2024-11-25 00:00:00" without FILETIME conversion, and any I/O field formatted as Date/Time will interpret the string as an invalid date and display 12:00:00. Open Tag Management, right-click the tag, and verify the Type column.
Verification and Diagnostic Logging
After deploying the script, verify with the following checklist:
- Open the WinCC Tag Management and confirm
BeginDateis of type "Date/Time" with a correct update cycle (typically 1 s for tag logging or 500 ms for fast HMI feedback). - Launch the runtime and select three different dates in the Calendar ActiveX. The internal tag must update on each click.
- Open the diagnostics window (Ctrl+Alt+D in WinCC Explorer) and inspect the
DiagCalendarWriteErrortag value. A nonzero value indicates a SetTagChar or parse failure. - Use the WinCC Channel Diagnosis tool (Start > Programs > Siemens Automation > SIMATIC > WinCC > Tools > Channel Diagnosis) to confirm the tag is being broadcast on the configured connection (MPI, PROFIBUS, PROFINET, EtherNet/IP, or TCP/IP).
- Export the tag value via the WinCC User Archive or OPC UA server and verify the FILETIME value corresponds to the expected date and 23:59:59 / 00:00:00 time. A correct 00:00:00 writes as a FILETIME value with the trailing 24 bits zero (since 23:59:59.9999999 has all bits set).
- Cross-check with a PLC DATE_AND_TIME block on the S7 side. If the PLC displays a different date or time, the connection mapping is incorrect; verify the tag's address mapping in the Connection properties dialog.
Add temporary logging by writing the constructed string to an internal String tag:
SetTagChar("DiagLastCalendarValue", datetime);
This allows field engineers to inspect the exact string passed to SetTagChar without attaching a debugger. For long-term monitoring, log the diagnostic tag and the constructed string to a WinCC Tag Logging archive with a 1-second acquisition cycle and a 24-hour retention window.
Troubleshooting Matrix
| Symptom | Root Cause | Diagnostic Step | Fix |
|---|---|---|---|
| Tag displays 12:00:00 only | Calendar Value passed directly to SetTagChar without ISO 8601 reformat | Write the intermediate string to a String tag and inspect | Use sprintf with %04d-%02d-%02d and append the time literal |
| 00:00:00 reverts to 12:00:00 | I/O field Output format missing HH:mm:ss | Inspect I/O field Output/Input tab in Properties dialog | Add HH:mm:ss to Output format, use uppercase HH for 24-hour |
| SetTagChar returns FALSE | Tag not defined, wrong type, or not connected | Open Tag Management, verify name, type, and Connection | Recreate tag as Date/Time internal with valid Connection |
| Date format wrong on PLC side | Locale mismatch between engineering and runtime PC | Compare dia buffer to runtime locale with GetTagChar | Force ISO 8601 with sscanf and dispatch on separator |
| Calendar control not updating | MSCAL.OCX not registered on runtime PC | Run regsvr32 /u mscal.ocx then regsvr32 mscal.ocx | Re-register MSCAL.OCX, copy to System32 or SysWOW64 |
| Script compile error: implicit declaration | Missing apdefap.h include | Check script header for #include "apdefap.h" | Add #include "apdefap.h" at top of script |
| Tag updates but PLC does not receive | Connection not configured for tag group | Tag Management > Properties > Connection | Assign tag to active connection, check PLC partner address |
| Script fails silently on calendar click | Event handler attached to wrong event | Inspect Properties > Events list for C-Action | Attach C-Action to ValueChanged or Click event |
| Tag displays 12:00:00 AM in US locale | I/O field uses lowercase hh format code | Open I/O field format editor with F8 | Change hh to HH for 24-hour format |
| Compile error CS0008: declaration after statement | C89 strict mode requires declarations at top | Move all variable declarations to top of function | Reorder declarations before first executable statement |
Alternative Implementation in VBScript
On WinCC Professional V15.1 and later, VBScript is the recommended scripting language and is fully supported alongside C-Script. The equivalent VBScript implementation is more concise because VBScript handles type coercion and locale-aware date parsing natively:
Function OnCalendarChange(ByVal lpszPictureName, ByVal lpszObjectName, ByVal lpszPropertyName)
Dim dia
Dim dt
Dim iso
dia = HMIRuntime.Tags("CalendarBeginDate").Read ' for SmartTag read
dia = GetPropChar(lpszPictureName, "CalendarBeginDate", "Value")
If IsNull(dia) Or Len(dia) = 0 Then
HMIRuntime.Tags("DiagCalendarWriteError").Write 1
Exit Function
End If
dt = CDate(dia)
iso = Year(dt) & "-" & Right("0" & Month(dt), 2) & "-" & Right("0" & Day(dt), 2) & " 00:00:00"
If HMIRuntime.Tags("BeginDate").Write(iso) = False Then
HMIRuntime.Tags("DiagCalendarWriteError").Write 3
Else
HMIRuntime.Tags("DiagCalendarWriteError").Write 0
End If
End Function
The CDate function performs locale-aware parsing and avoids the manual sscanf dispatch. The downside is that VBScript performance is approximately 5-10x slower than C-Script for high-frequency tag updates; for a calendar click that fires once per user interaction, this is irrelevant.
Integration with S7 PLC DATE_AND_TIME
When the Date/Time tag is mapped to an S7-1500 or S7-1200 PLC, the WinCC tag uses the DTL or DATE_AND_TIME data type on the PLC side. The mapping in WinCC Tag Management requires:
- Tag type: Date/Time
- PLC address: DBx.DBBy or DBx.DBB0 (8 bytes for DTL: year, month, day, hour, minute, second, millisecond, weekday)
- Conversion: WinCC handles the FILETIME-to-DTL conversion automatically when the tag type is Date/Time.
Verify the round-trip by writing 00:00:00 from the HMI and reading the DTL value back via TIA Portal watch table. A correct write shows DTL#2024-11-25-00:00:00.0. An incorrect write shows DTL#2024-11-25-12:00:00.0 confirming the 12:00:00 fallback in the parser.
FAQ
Why does the Date/Time tag always display 12:00:00 after a Calendar Control write?
The Calendar Control's Value property returns a locale-specific date string without a time component. When SetTagChar receives an unrecognized format, WinCC's Date/Time parser falls back to a date-only interpretation and uses 12:00:00 noon as the default. Reformat the date to ISO 8601 (YYYY-MM-DD) with sprintf and append a literal time string before calling SetTagChar.
How do I write a literal 00:00:00 time to a WinCC Date/Time tag?
Construct the string with sprintf("%s 00:00:00", datebuf) where datebuf holds the ISO 8601 date. Do not concatenate string literals across multiple C statements. Verify the I/O field Output format includes uppercase HH:mm:ss so the displayed value does not revert to 12:00:00 or 12:00:00 AM.
What is the difference between SetTagChar and SetPropChar?
SetTagChar writes to an internal WinCC tag in the Tag Management database and triggers standard tag distribution to the PLC, archive, and OPC UA server. SetPropChar writes to a property of a graphic object on the current picture and only affects the local display. Use SetTagChar for values that must leave the HMI and SetPropChar for visual-only updates.
Can I read a Date/Time tag as a string from C-Script?
Yes. Use GetTagChar("BeginDate") to retrieve the tag in ISO 8601 format (YYYY-MM-DD HH:MM:SS). The returned pointer is valid until the next C-Script API call on the same thread; copy the buffer to a project-owned char array if subsequent API calls are required within the same function.
Which WinCC versions support the Calendar Control 11.0 ActiveX?
Microsoft Calendar Control 11.0 (MSCAL.OCX) is supported on WinCC V7.0 through V7.5 SP2 and on WinCC Professional V13 SP1 through V18 in TIA Portal. It is not available on WinCC Comfort Panels or Basic Panels. For these panels, use the built-in Date/Time Picker control from the toolbox, or configure the tag through the tag simulation dialog.
How do I handle different date locales on a single runtime?
Inspect the separator character at offset 2 of the Calendar Value string and dispatch the sscanf format accordingly. Hyphen indicates ISO 8601, slash indicates US or French format, period indicates German format. The separator-based dispatch avoids hard-coding a locale and supports regional engineering stations.
Why does my script compile with implicit declaration errors?
The WinCC C-Script compiler requires #include "apdefap.h" at the top of every script to declare the WinCC API prototypes. Without the include, GetPropChar, SetTagChar, sscanf, and sprintf are undeclared and the compiler emits implicit declaration warnings that may or may not be promoted to errors depending on the WinCC version.