/* ###################################################################
**     This component module is generated by Processor Expert. Do not modify it.
**     Filename    : McuTimeDate.h
**     Project     : FRDM-K64F_Generator
**     Processor   : MK64FN1M0VLL12
**     Component   : GenericTimeDate
**     Version     : Component 01.064, Driver 01.00, CPU db: 3.00.000
**     Compiler    : GNU C Compiler
**     Date/Time   : 2020-08-10, 19:29, # CodeGen: 671
**     Abstract    :
**         Software date/time module.
**     Settings    :
**          Component name                                 : McuTimeDate
**          Software RTC                                   : Enabled
**            Tick Time (ms)                               : 10
**            RTOS                                         : Enabled
**              RTOS                                       : McuRTOS
**          Hardware RTC                                   : Enabled
**            Internal                                     : Disabled
**            External RTC                                 : Disabled
**          Set Time and Date                              : 
**            Software RTC                                 : yes
**            Internal RTC                                 : no
**            External RTC                                 : yes
**          Get Time and Date                              : Software RTC
**          Init()                                         : 
**            Defaults                                     : 
**              Time                                       : 17:51:31
**              Date                                       : 2019-08-01
**            Call Init() in startup                       : yes
**            Software RTC Initialization                  : Init from Defaults
**          System                                         : 
**            Critical Section                             : McuCriticalSection
**            SDK                                          : McuLib
**            Shell                                        : Enabled
**              Utility                                    : McuUtility
**              Shell                                      : McuShell
**     Contents    :
**         AddTick                     - void McuTimeDate_AddTick(void);
**         AddTicks                    - void McuTimeDate_AddTicks(uint16_t nofTicks);
**         TicksToTime                 - uint8_t McuTimeDate_TicksToTime(uint32_t ticks, TIMEREC *time);
**         TimeToTicks                 - uint8_t McuTimeDate_TimeToTicks(TIMEREC *time, uint32_t *ticks);
**         CalculateDayOfWeek          - uint8_t McuTimeDate_CalculateDayOfWeek(uint16_t Year, uint8_t Month, uint8_t...
**         SetTime                     - uint8_t McuTimeDate_SetTime(uint8_t Hour, uint8_t Min, uint8_t Sec, uint8_t...
**         GetTime                     - uint8_t McuTimeDate_GetTime(TIMEREC *time);
**         SetDate                     - uint8_t McuTimeDate_SetDate(uint16_t Year, uint8_t Month, uint8_t Day);
**         GetDate                     - uint8_t McuTimeDate_GetDate(DATEREC *date);
**         SetTimeDate                 - uint8_t McuTimeDate_SetTimeDate(TIMEREC *time, DATEREC *date);
**         GetTimeDate                 - uint8_t McuTimeDate_GetTimeDate(TIMEREC *time, DATEREC *date);
**         SetSWTimeDate               - uint8_t McuTimeDate_SetSWTimeDate(TIMEREC *time, DATEREC *date);
**         GetSWTimeDate               - uint8_t McuTimeDate_GetSWTimeDate(TIMEREC *time, DATEREC *date);
**         SetInternalRTCTimeDate      - uint8_t McuTimeDate_SetInternalRTCTimeDate(TIMEREC *time, DATEREC *date);
**         GetInternalRTCTimeDate      - uint8_t McuTimeDate_GetInternalRTCTimeDate(TIMEREC *time, DATEREC *date);
**         SyncWithInternalRTC         - uint8_t McuTimeDate_SyncWithInternalRTC(void);
**         SyncSWtimeToInternalRTCsec  - uint8_t McuTimeDate_SyncSWtimeToInternalRTCsec(void);
**         SetExternalRTCTimeDate      - uint8_t McuTimeDate_SetExternalRTCTimeDate(TIMEREC *time, DATEREC *date);
**         GetExternalRTCTimeDate      - uint8_t McuTimeDate_GetExternalRTCTimeDate(TIMEREC *time, DATEREC *date);
**         SyncWithExternalRTC         - uint8_t McuTimeDate_SyncWithExternalRTC(void);
**         UnixSecondsToTimeDateCustom - void McuTimeDate_UnixSecondsToTimeDateCustom(int32_t seconds, int8_t...
**         UnixSecondsToTimeDate       - void McuTimeDate_UnixSecondsToTimeDate(int32_t seconds, int8_t offset_hours,...
**         TimeDateToUnixSecondsCustom - int32_t McuTimeDate_TimeDateToUnixSecondsCustom(TIMEREC *time, DATEREC *date,...
**         TimeDateToUnixSeconds       - int32_t McuTimeDate_TimeDateToUnixSeconds(TIMEREC *time, DATEREC *date,...
**         AddDateString               - uint8_t McuTimeDate_AddDateString(uint8_t *buf, size_t bufSize, DATEREC...
**         AddTimeString               - uint8_t McuTimeDate_AddTimeString(uint8_t *buf, size_t bufSize, TIMEREC...
**         ParseCommand                - uint8_t McuTimeDate_ParseCommand(const unsigned char *cmd, bool *handled,...
**         DeInit                      - void McuTimeDate_DeInit(void);
**         Init                        - uint8_t McuTimeDate_Init(void);
**
** * Copyright (c) 2011-2020, Erich Styger
**  * Web:         https://mcuoneclipse.com
**  * SourceForge: https://sourceforge.net/projects/mcuoneclipse
**  * Git:         https://github.com/ErichStyger/McuOnEclipse_PEx
**  * All rights reserved.
**  *
**  * Redistribution and use in source and binary forms, with or without modification,
**  * are permitted provided that the following conditions are met:
**  *
**  * - Redistributions of source code must retain the above copyright notice, this list
**  *   of conditions and the following disclaimer.
**  *
**  * - Redistributions in binary form must reproduce the above copyright notice, this
**  *   list of conditions and the following disclaimer in the documentation and/or
**  *   other materials provided with the distribution.
**  *
**  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
**  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
**  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
**  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
**  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
**  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
**  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
**  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
**  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
**  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
** ###################################################################*/
/*!
** @file McuTimeDate.h
** @version 01.00
** @brief
**         Software date/time module.
*/         
/*!
**  @addtogroup McuTimeDate_module McuTimeDate module documentation
**  @{
*/         

/* MODULE McuTimeDate. */

#include "McuTimeDate.h"
#include <stdlib.h> /* for NULL */
#if McuTimeDate_CONFIG_USE_EXTERNAL_HW_RTC
  #include "McuExtRTC.h"
#endif


#if McuTimeDate_TICK_TIME_MS==0
  #error "Tick period cannot be zero!"
#endif
#define McuTimeDate_TICKS_PER_S  (1000/McuTimeDate_TICK_TIME_MS) /* number of timer ticks per second */

#if McuTimeDate_CONFIG_USE_SOFTWARE_RTC
  /* counters used by software RTC */
  static uint8_t CntDay;               /* Day counter */
  static uint8_t CntMonth;             /* Month counter */
  static uint16_t CntYear;             /* Year Counter */
  static uint32_t tickCntr;            /* Software tick counter (1 tick = McuTimeDate_TICK_TIME_MS ms) */
#endif

/* Table of month length (in days) */
static const uint8_t ULY[12] = {31U,28U,31U,30U,31U,30U,31U,31U,30U,31U,30U,31U}; /* Un-leap-year */
static const uint8_t  LY[12] = {31U,29U,31U,30U,31U,30U,31U,31U,30U,31U,30U,31U}; /* Leap-year */

static uint8_t AddDateToBuf(uint8_t *buf, uint16_t bufSize, DATEREC *tdate) {
  McuUtility_strcatNum16uFormatted(buf, bufSize, tdate->Day, '0', 2);
  McuUtility_chcat(buf, bufSize, '.');
  McuUtility_strcatNum16uFormatted(buf, bufSize, tdate->Month, '0', 2);
  McuUtility_chcat(buf, bufSize, '.');
  McuUtility_strcatNum16uFormatted(buf, bufSize, (uint16_t)tdate->Year, '0', 2);
  return ERR_OK;
}

static uint8_t AddTimeToBuf(uint8_t *buf, uint16_t bufSize, TIMEREC *ttime) {
  McuUtility_strcatNum16sFormatted(buf, bufSize, ttime->Hour, '0', 2);
  McuUtility_chcat(buf, bufSize, ':');
  McuUtility_strcatNum16sFormatted(buf, bufSize, ttime->Min, '0', 2);
  McuUtility_chcat(buf, bufSize, ':');
  McuUtility_strcatNum16sFormatted(buf, bufSize, ttime->Sec, '0', 2);
  McuUtility_chcat(buf, bufSize, ',');
#if McuTimeDate_HAS_SEC100_IN_TIMEREC
  McuUtility_strcatNum16sFormatted(buf, bufSize, ttime->Sec100, '0', 2);
#else
  McuUtility_strcatNum16sFormatted(buf, bufSize, 0, '0', 2);
#endif
  return ERR_OK;
}

static uint8_t AddDate(uint8_t *buf, uint16_t bufSize, uint8_t (*GetTimeDateFn)(TIMEREC*, DATEREC*)) {
  DATEREC tdate;

  if (GetTimeDateFn(NULL, &tdate)!=ERR_OK) {
    return ERR_FAILED;
  }
  return AddDateToBuf(buf, bufSize, &tdate);
}

static uint8_t AddTime(uint8_t *buf, uint16_t bufSize, uint8_t (*GetTimeDateFn)(TIMEREC*, DATEREC*)) {
  TIMEREC ttime;

  if (GetTimeDateFn(&ttime, NULL)!=ERR_OK) {
    return ERR_FAILED;
  }
  return AddTimeToBuf(buf, bufSize, &ttime);
}

static uint8_t DateCmd(const unsigned char *cmd, McuShell_ConstStdIOType *io) {
  /* precondition: cmd points to "McuTimeDate date" */
  uint8_t day, month;
  uint16_t year;
  const unsigned char *p;
  uint8_t res = ERR_OK;

  p = cmd + sizeof("McuTimeDate date")-1;
  if (*p==' ') { /* ok, have an argument */
    if (McuUtility_ScanDate(&p, &day, &month, &year) == ERR_OK) { /* ok, format fine */
      /* update software real time clock */
      res = McuTimeDate_SetDate(year, month, day);
      if (res != ERR_OK) {
        McuShell_SendStr((unsigned char*)"*** Failure setting date\r\n", io->stdErr);
        res = ERR_FAILED;
      }
    } else {
      McuShell_SendStr((unsigned char*)"*** error while reading command! ***", io->stdErr);
      McuShell_SendStr((void *)cmd, io->stdErr);
      McuShell_SendStr((unsigned char*)"\r\n", io->stdErr);
      res = ERR_FAILED;
    }
  } /* has an argument */
  /* print now current date */
  if (res==ERR_OK) {
    unsigned char buf[sizeof("RTC: Wednesday dd:mm:yyyy\\r\\n")];

    McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"RTC: ");
    if (AddDate(buf, sizeof(buf), McuTimeDate_GetTimeDate)!=ERR_OK) {
      McuShell_SendStr((unsigned char*)"***Failed to get date\r\n", io->stdErr);
      res = ERR_FAILED;
    } else {
      McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n");
      McuShell_SendStr(buf, io->stdOut);
    }
  }
  return res;
}

static uint8_t TimeCmd(const unsigned char *cmd, McuShell_ConstStdIOType *io) {
  uint8_t hour, minute, second, hSecond;
  const unsigned char *p;
  uint8_t res = ERR_OK;

  p = cmd + sizeof("McuTimeDate time")-1;
  if (*p==' ') { /* has an argument */
    if (McuUtility_ScanTime(&p, &hour, &minute, &second, &hSecond)==ERR_OK) { /* format fine */
      /* set SW RTC time */
      res = McuTimeDate_SetTime(hour, minute, second, hSecond);
      if (res != ERR_OK) {
        McuShell_SendStr((unsigned char*)"*** Failure setting time\r\n", io->stdErr);
        res = ERR_FAILED;
      }
    } else {
      McuShell_SendStr((unsigned char*)"*** Error while reading command: ", io->stdErr);
      McuShell_SendStr(cmd, io->stdErr);
      McuShell_SendStr((unsigned char*)"\r\n", io->stdErr);
      res = ERR_FAILED;
    }
  }
  /* print now current time */
  if (res==ERR_OK) {
    unsigned char buf[sizeof("RTC: hh:mm:ss.hh\\r\\n")+5];

    McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"RTC: ");
    if (AddTime(buf, sizeof(buf), McuTimeDate_GetTimeDate)!=ERR_OK) {
      McuShell_SendStr((unsigned char*)"***Failed to get time\r\n", io->stdErr);
      res = ERR_FAILED;
    } else {
      McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n");
      McuShell_SendStr(buf, io->stdOut);
    }
  }
  return res;
}

static uint8_t PrintStatus(McuShell_ConstStdIOType *io) {
  uint8_t buf[24];

  McuShell_SendStatusStr((unsigned char*)"McuTimeDate", (const unsigned char*)"Date and time information\r\n", io->stdOut);
#if McuTimeDate_CONFIG_USE_SOFTWARE_RTC
  McuShell_SendStatusStr((unsigned char*)"  SW RTC", (const unsigned char*)"", io->stdOut);
  buf[0] = '\0';
  if (AddDate(buf, sizeof(buf), McuTimeDate_GetSWTimeDate)!=ERR_OK) {
    McuShell_SendStr((unsigned char*)"***Failed to get date!\r\n", io->stdErr);
    return ERR_FAILED;
  }
  McuShell_SendStr(buf, io->stdOut);
  McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)", ");
  if (AddTime(buf, sizeof(buf), McuTimeDate_GetSWTimeDate)!=ERR_OK) {
    McuShell_SendStr((unsigned char*)"***Failed to get time!\r\n", io->stdErr);
    return ERR_FAILED;
  } else {
    McuShell_SendStr(buf, io->stdOut);
  }
  McuShell_SendStr((unsigned char*)"\r\n", io->stdOut);
#endif
#if McuTimeDate_CONFIG_USE_INTERNAL_HW_RTC_LDD || McuTimeDate_CONFIG_USE_INTERNAL_HW_RTC_BEAN
  McuShell_SendStatusStr((unsigned char*)"  HW RTC", (const unsigned char*)"", io->stdOut);
  buf[0] = '\0';
  if (AddDate(buf, sizeof(buf), McuTimeDate_GetInternalRTCTimeDate)!=ERR_OK) {
    McuShell_SendStr((unsigned char*)"***Failed to get internal RTC date!\r\n", io->stdErr);
    return ERR_FAILED;
  }
  McuShell_SendStr(buf, io->stdOut);
  McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)", ");
  if (AddTime(buf, sizeof(buf), McuTimeDate_GetInternalRTCTimeDate)!=ERR_OK) {
    McuShell_SendStr((unsigned char*)"***Failed to get internal RTC time!\r\n", io->stdErr);
    return ERR_FAILED;
  } else {
    McuShell_SendStr(buf, io->stdOut);
  }
  McuShell_SendStr((unsigned char*)"\r\n", io->stdOut);
#endif
#if McuTimeDate_CONFIG_USE_EXTERNAL_HW_RTC
  McuShell_SendStatusStr((unsigned char*)"  Ext. RTC", (const unsigned char*)"", io->stdOut);
  buf[0] = '\0';
  if (AddDate(buf, sizeof(buf), McuTimeDate_GetExternalRTCTimeDate)!=ERR_OK) {
    McuShell_SendStr((unsigned char*)"***Failed to get external RTC date!\r\n", io->stdErr);
    return ERR_FAILED;
  }
  McuShell_SendStr(buf, io->stdOut);
  McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)", ");
  if (AddTime(buf, sizeof(buf), McuTimeDate_GetExternalRTCTimeDate)!=ERR_OK) {
    McuShell_SendStr((unsigned char*)"***Failed to get external RTC time!\r\n", io->stdErr);
    return ERR_FAILED;
  } else {
    McuShell_SendStr(buf, io->stdOut);
  }
  McuShell_SendStr((unsigned char*)"\r\n", io->stdOut);
#endif
  return ERR_OK;
}

/*
** ===================================================================
**     Method      :  AddTick (component GenericTimeDate)
**
**     Description :
**         Increments the tick counter of the software RTC. Needs to be
**         called periodically by the application to increase the time
**         tick count.
**     Parameters  : None
**     Returns     : Nothing
** ===================================================================
*/
void McuTimeDate_AddTick(void)
{
#if McuTimeDate_CONFIG_USE_SOFTWARE_RTC
  const uint8_t *ptr;                  /* Pointer to ULY/LY table */
  McuCriticalSection_CriticalVariable()

  McuCriticalSection_EnterCritical();  /* need exclusive access to tick counter */
  tickCntr++;                          /* Software timer counter increment by timer period */
  McuCriticalSection_ExitCritical();   /* end of critical section */
  if (tickCntr >= 24*3600UL*McuTimeDate_TICKS_PER_S) { /* Does the counter reach 24 hours? */
    tickCntr -= 24*3600UL*McuTimeDate_TICKS_PER_S; /* If yes then reset it by subtracting exactly 24 hours */
    McuCriticalSection_EnterCritical();
    CntDay++;                          /* Increment day counter */
    McuCriticalSection_ExitCritical();
    if (CntYear & 0x03U) {             /* Is this year un-leap-one? */
      ptr = ULY;                       /* Set pointer to un-leap-year day table */
    } else {                           /* Is this year leap-one? */
      ptr = LY;                        /* Set pointer to leap-year day table */
    }
    ptr--;                             /* Decrement the pointer */
    if (CntDay > ptr[CntMonth]) {      /* Day counter overflow? */
      CntDay = 1U;                     /* Set day counter on 1 */
      McuCriticalSection_EnterCritical();
      CntMonth++;                      /* Increment month counter */
      McuCriticalSection_ExitCritical();
      if (CntMonth > 12U) {            /* Month counter overflow? */
        CntMonth = 1U;                 /* Set month counter on 1 */
        McuCriticalSection_EnterCritical();
        CntYear++;                     /* Increment year counter */
        McuCriticalSection_ExitCritical();
      }
    }
  }
#endif
}

/*
** ===================================================================
**     Method      :  AddTicks (component GenericTimeDate)
**
**     Description :
**         Same as AddTick(), but multiple ticks can be added in one
**         step.
**     Parameters  :
**         NAME            - DESCRIPTION
**         nofTicks        - Number of ticks to be added
**     Returns     : Nothing
** ===================================================================
*/
void McuTimeDate_AddTicks(uint16_t nofTicks)
{
#if McuTimeDate_CONFIG_USE_SOFTWARE_RTC
  while(nofTicks>0) {
    McuTimeDate_AddTick();
    nofTicks--;
  }
#else
  (void)nofTicks; /* not used */
#endif
}

/*
** ===================================================================
**     Method      :  SetSWTimeDate (component GenericTimeDate)
**
**     Description :
**         This method sets a new actual time and date of the software
**         RTC.
**     Parameters  :
**         NAME            - DESCRIPTION
**       * time            - Pointer to struct which contains the
**                           time to be set. Can be NULL if time shall
**                           not be set.
**       * date            - Pointer to struct which contains the
**                           date information to be set. Can be NULL if
**                           date shall not be set.
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuTimeDate_SetSWTimeDate(TIMEREC *time, DATEREC *date)
{
#if McuTimeDate_CONFIG_USE_SOFTWARE_RTC
  uint8_t res;
  uint32_t nofTicks;
  bool failed = FALSE;
  McuCriticalSection_CriticalVariable()

  if (time!=NULL) {
    res = McuTimeDate_TimeToTicks(time, &nofTicks);
    if (res!=ERR_OK) {
      failed = TRUE;
    }
    if (res==ERR_OK) {
      McuCriticalSection_EnterCritical();
      tickCntr = nofTicks;
      McuCriticalSection_ExitCritical();
    }
  }
  if (date!=NULL) {
    McuCriticalSection_EnterCritical();
    CntDay = date->Day;
    CntMonth = date->Month;
    CntYear = date->Year;
    McuCriticalSection_ExitCritical();
  }
  if (failed) {
    return ERR_FAILED;
  }
  return ERR_OK;
#else
  return ERR_FAILED;
#endif
}

/*
** ===================================================================
**     Method      :  GetSWTimeDate (component GenericTimeDate)
**
**     Description :
**         This method returns current time and date from the software
**         RTC.
**     Parameters  :
**         NAME            - DESCRIPTION
**       * time            - Pointer to the structure to return the
**                           time. Can be NULL.
**       * date            - Pointer to structure which returns the
**                           date information. Can be NULL
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuTimeDate_GetSWTimeDate(TIMEREC *time, DATEREC *date)
{
#if McuTimeDate_CONFIG_USE_SOFTWARE_RTC
  uint8_t res;
  uint32_t ticks;
  bool failed = FALSE;
  McuCriticalSection_CriticalVariable()

  if (time!=NULL) {
    McuCriticalSection_EnterCritical();
    ticks = tickCntr;
    McuCriticalSection_ExitCritical();
    res = McuTimeDate_TicksToTime(ticks, time);
    if (res!=ERR_OK) {
      failed = TRUE;
    }
  }
  if (date!=NULL) {
    McuCriticalSection_EnterCritical();
    date->Year = CntYear;
    date->Month = CntMonth;
    date->Day = CntDay;
    McuCriticalSection_ExitCritical();
  }
  if (failed) {
    return ERR_FAILED;
  }
  return ERR_OK;
#else
  return ERR_FAILED;
#endif
}

/*
** ===================================================================
**     Method      :  SetTime (component GenericTimeDate)
**
**     Description :
**         This method sets a new actual time of the software RTC.
**     Parameters  :
**         NAME            - DESCRIPTION
**         Hour            - Hours (0 - 23)
**         Min             - Minutes (0 - 59)
**         Sec             - Seconds (0 - 59)
**         Sec100          - Hundredth of seconds (0 - 99)
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
#ifdef __HIWARE__
  #pragma MESSAGE DISABLE C5905 /* multiplication with one (happens if McuTimeDate_TICKS_PER_S is 100) */
#endif
uint8_t McuTimeDate_SetTime(uint8_t Hour, uint8_t Min, uint8_t Sec, uint8_t Sec100)
{
  uint8_t res = ERR_FAILED;
  TIMEREC time;
  bool failed = FALSE;

  if ((Sec100>99U) || (Sec>59U) || (Min>59U) || (Hour>23U)) {
    return ERR_RANGE;
  }
  time.Hour = Hour;
  time.Min = Min;
  time.Sec = Sec;
#if McuTimeDate_HAS_SEC100_IN_TIMEREC
  time.Sec100 = Sec100;
#endif
#if McuTimeDate_CONFIG_USE_SOFTWARE_RTC && McuTimeDate_CONFIG_SET_TIME_DATE_METHOD_USES_SOFTWARE_RTC
  res = McuTimeDate_SetSWTimeDate(&time, NULL);
  if (res!=ERR_OK) {
    failed = TRUE;
  }
#endif
#if McuTimeDate_CONFIG_USE_INTERNAL_HW_RTC && McuTimeDate_CONFIG_SET_TIME_DATE_METHOD_USES_INTERNAL_RTC
  res = McuTimeDate_SetInternalRTCTimeDate(&time, NULL);
  if (res!=ERR_OK) {
    failed = TRUE;
  }
#endif
#if McuTimeDate_CONFIG_USE_EXTERNAL_HW_RTC && McuTimeDate_CONFIG_SET_TIME_DATE_METHOD_USES_EXTERNAL_RTC
  res = McuTimeDate_SetExternalRTCTimeDate(&time, NULL);
  if (res!=ERR_OK) {
    failed = TRUE;
  }
#endif
#if McuTimeDate_ON_TIME_SET_EVENT
  McuTimeDate_ON_TIME_SET_EVENT_NAME(Hour, Min, Sec, Sec100); /* call user event */
#endif
  if (failed) {
    res = ERR_FAILED;
  }
  return res;
}
#ifdef __HIWARE__
  #pragma MESSAGE DEFAULT C5905 /* multiplication with one  */
#endif

/*
** ===================================================================
**     Method      :  GetTime (component GenericTimeDate)
**
**     Description :
**         This method returns current time from the software RTC.
**     Parameters  :
**         NAME            - DESCRIPTION
**       * time            - Pointer to the structure TIMEREC. It
**                           contains actual number of hours, minutes,
**                           seconds and hundredth of seconds.
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuTimeDate_GetTime(TIMEREC *time)
{
  uint8_t res;

#if McuTimeDate_CONFIG_USE_SOFTWARE_RTC && McuTimeDate_CONFIG_USE_GET_TIME_DATE_METHOD==McuTimeDate_GET_TIME_DATE_METHOD_SOFTWARE_RTC
  res = McuTimeDate_GetSWTimeDate(time, NULL);
#elif McuTimeDate_CONFIG_USE_INTERNAL_HW_RTC && McuTimeDate_CONFIG_USE_GET_TIME_DATE_METHOD==McuTimeDate_GET_TIME_DATE_METHOD_INTERNAL_RTC
  res = McuTimeDate_GetInternalRTCTimeDate(time, NULL);
#elif McuTimeDate_CONFIG_USE_EXTERNAL_HW_RTC && McuTimeDate_CONFIG_USE_GET_TIME_DATE_METHOD==McuTimeDate_GET_TIME_DATE_METHOD_EXTERNAL_RTC
  res = McuTimeDate_GetExternalRTCTimeDate(time, NULL);
#else
  #error "invalid configuration!"
  res = ERR_FAILED;
#endif
  /* call user event */
#if McuTimeDate_ON_TIME_GET_EVENT
  #if McuTimeDate_HAS_SEC100_IN_TIMEREC
    McuTimeDate_ON_TIME_GET_EVENT_NAME(&time->Hour, &time->Min, &time->Sec, &time->Sec100);
  #else
    McuTimeDate_ON_TIME_GET_EVENT_NAME(&time->Hour, &time->Min, &time->Sec, 0);
  #endif
#endif
  return res;
}

/*
** ===================================================================
**     Method      :  SetDate (component GenericTimeDate)
**
**     Description :
**         This method sets a new actual date of the software RTC.
**     Parameters  :
**         NAME            - DESCRIPTION
**         Year            - Years (16-bit unsigned integer)
**         Month           - Months (8-bit unsigned integer)
**         Day             - Days (8-bit unsigned integer)
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuTimeDate_SetDate(uint16_t Year, uint8_t Month, uint8_t Day)
{
  const uint8_t* ptr;                  /* Pointer to ULY/LY table */
  uint8_t res = ERR_FAILED;
  bool failed = FALSE;
  DATEREC date;

  if ((Year < 1998U) || (Year > 2099U) || (Month > 12U) || (Month == 0U) || (Day > 31U) || (Day == 0U)) {
    return ERR_RANGE;
  }
  ptr = (((Year&0x03U)!=0U)?ULY:LY); /* Set pointer to leap-year or un-leap-year day table */
  if (ptr[Month - 1U] < Day) { /* Does the obtained number of days exceed number of days in the appropriate month & year? */
    return ERR_RANGE;
  }
  date.Year = Year;
  date.Month = Month;
  date.Day = Day;
#if McuTimeDate_CONFIG_USE_SOFTWARE_RTC && McuTimeDate_CONFIG_SET_TIME_DATE_METHOD_USES_SOFTWARE_RTC
  res = McuTimeDate_SetSWTimeDate(NULL, &date);
  if (res!=ERR_OK) {
    failed = TRUE;
  }
#endif
#if McuTimeDate_CONFIG_USE_INTERNAL_HW_RTC && McuTimeDate_CONFIG_SET_TIME_DATE_METHOD_USES_INTERNAL_RTC
  res = McuTimeDate_SetInternalRTCTimeDate(NULL, &date);
  if (res!=ERR_OK) {
    failed = TRUE;
  }
#endif
#if McuTimeDate_CONFIG_USE_EXTERNAL_HW_RTC && McuTimeDate_CONFIG_SET_TIME_DATE_METHOD_USES_EXTERNAL_RTC
  res = McuTimeDate_SetExternalRTCTimeDate(NULL, &date);
  if (res!=ERR_OK) {
    failed = TRUE;
  }
#endif
#if McuTimeDate_ON_DATE_SET_EVENT
  McuTimeDate_ON_DATE_SET_EVENT_NAME(Day, Month, Year); /* call user event */
#endif
  if (failed) {
    res = ERR_FAILED;
  }
  return res;
}

/*
** ===================================================================
**     Method      :  GetDate (component GenericTimeDate)
**
**     Description :
**         This method returns current date from the software RTC.
**     Parameters  :
**         NAME            - DESCRIPTION
**       * date            - Pointer to DATEREC
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuTimeDate_GetDate(DATEREC *date)
{
  uint8_t res;

#if McuTimeDate_CONFIG_USE_SOFTWARE_RTC && McuTimeDate_CONFIG_USE_GET_TIME_DATE_METHOD==McuTimeDate_GET_TIME_DATE_METHOD_SOFTWARE_RTC
  res = McuTimeDate_GetSWTimeDate(NULL, date);
#elif McuTimeDate_CONFIG_USE_INTERNAL_HW_RTC && McuTimeDate_CONFIG_USE_GET_TIME_DATE_METHOD==McuTimeDate_GET_TIME_DATE_METHOD_INTERNAL_RTC
  res = McuTimeDate_GetInternalRTCTimeDate(NULL, date);
#elif McuTimeDate_CONFIG_USE_EXTERNAL_HW_RTC && McuTimeDate_CONFIG_USE_GET_TIME_DATE_METHOD==McuTimeDate_GET_TIME_DATE_METHOD_EXTERNAL_RTC
  res = McuTimeDate_GetExternalRTCTimeDate(NULL, date);
#else
  #error "invalid configuration!"
  res = ERR_FAILED;
#endif
#if McuTimeDate_ON_DATE_GET_EVENT && McuTimeDate_CONFIG_USE_SOFTWARE_RTC
  McuTimeDate_ON_DATE_GET_EVENT_NAME(&CntDay, &CntMonth, &CntYear); /* call user event */
#endif
  return res;
}

/*
** ===================================================================
**     Method      :  Init (component GenericTimeDate)
**
**     Description :
**         Initialization method
**     Parameters  : None
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuTimeDate_Init(void)
{
  /* initialize software RTC time and date */
#if McuTimeDate_CONFIG_INIT_SOFTWARE_RTC_METHOD==McuTimeDate_INIT_SOFTWARE_RTC_FROM_DEFAULTS
/* default time/date values */
  TIMEREC McuTimeDate_DefaultTime = {
    McuTimeDate_CONFIG_DEFAULT_INITIAL_TIME_HOUR, /* hour */
    McuTimeDate_CONFIG_DEFAULT_INITIAL_TIME_MIN,  /* minute */
    McuTimeDate_CONFIG_DEFAULT_INITIAL_TIME_SEC,  /* second */
  #if McuTimeDate_HAS_SEC100_IN_TIMEREC
    0 /* h-second */
  #endif
  };
  DATEREC McuTimeDate_DefaultDate = {
    McuTimeDate_CONFIG_DEFAULT_INITIAL_DATE_YEAR, /* year */
    McuTimeDate_CONFIG_DEFAULT_INITIAL_DATE_MONTH,  /* month */
    McuTimeDate_CONFIG_DEFAULT_INITIAL_DATE_DAY /* day */
  };
  return McuTimeDate_SetSWTimeDate((TIMEREC*)&McuTimeDate_DefaultTime, (DATEREC*)&McuTimeDate_DefaultDate);
#elif McuTimeDate_CONFIG_INIT_SOFTWARE_RTC_METHOD==McuTimeDate_INIT_SOFTWARE_RTC_FROM_INTERNAL_RTC
  return McuTimeDate_SyncWithInternalRTC();
#elif McuTimeDate_CONFIG_INIT_SOFTWARE_RTC_METHOD==McuTimeDate_INIT_SOFTWARE_RTC_FROM_EXTERNAL_RTC
  return McuTimeDate_SyncWithExternalRTC();
#else
  return ERR_FAILED; /* wrong or no init specified? */
#endif
}

/*
** ===================================================================
**     Method      :  ParseCommand (component GenericTimeDate)
**
**     Description :
**         Shell Command Line parser
**     Parameters  :
**         NAME            - DESCRIPTION
**       * cmd             - Pointer to command line
**       * handled         - Pointer to variable which tells if
**                           the command has been handled or not
**       * io              - Pointer to I/O structure
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuTimeDate_ParseCommand(const unsigned char *cmd, bool *handled, const McuShell_StdIOType *io)
{
  const uint8_t *p;
  uint8_t hour, minute, second, hsec;
  uint8_t month, day;
  uint16_t year;
  TIMEREC time;
  DATEREC date;
  int32_t unixSecs;
  uint8_t buf[16];

  if (McuUtility_strcmp((char*)cmd, McuShell_CMD_HELP)==0 || McuUtility_strcmp((char*)cmd, "McuTimeDate help")==0) {
    McuShell_SendHelpStr((unsigned char*)"McuTimeDate", (const unsigned char*)"Group of McuTimeDate commands\r\n", io->stdOut);
    McuShell_SendHelpStr((unsigned char*)"  help|status", (const unsigned char*)"Print help or status information\r\n", io->stdOut);
    McuShell_SendHelpStr((unsigned char*)"  time [hh:mm:ss[,z]]", (const unsigned char*)"Set the current time. Prints the current time if no argument\r\n", io->stdOut);
    McuShell_SendHelpStr((unsigned char*)"  date [dd.mm.yyyy]", (const unsigned char*)"Set the current date. Prints the current date if no argument\r\n", io->stdOut);
    McuShell_SendHelpStr((unsigned char*)"  dateToSec <datetime>", (const unsigned char*)"Convert date/time int UNIX timestamp (seconds after 1970)\r\n", io->stdOut);
    McuShell_SendHelpStr((unsigned char*)"  secToDate <secs>", (const unsigned char*)"Convert UNIX timestamp to date/time\r\n", io->stdOut);
    *handled = TRUE;
    return ERR_OK;
  } else if ((McuUtility_strcmp((char*)cmd, McuShell_CMD_STATUS)==0) || (McuUtility_strcmp((char*)cmd, "McuTimeDate status")==0)) {
    *handled = TRUE;
    return PrintStatus(io);
  } else if (McuUtility_strncmp((char*)cmd, "McuTimeDate secToDate ", sizeof("McuTimeDate secToDate ")-1)==0) {
    *handled = TRUE;
    p = cmd+sizeof("McuTimeDate secToDate ")-1;
    if (McuUtility_ScanDecimal32sNumber(&p, &unixSecs)==ERR_OK) {
      McuTimeDate_UnixSecondsToTimeDate(unixSecs, 0, &time, &date);
      buf[0] = '\0';
      AddDateToBuf(buf, sizeof(buf), &date);
      McuShell_SendStr(buf, io->stdOut);
      McuShell_SendStr((uint8_t*)" ", io->stdOut);
      buf[0] = '\0';
      AddTimeToBuf(buf, sizeof(buf), &time);
      McuShell_SendStr(buf, io->stdOut);
      McuShell_SendStr((uint8_t*)"\r\n", io->stdOut);
    } else {
      McuShell_SendStr((uint8_t*)"Wrong second format.\r\n", io->stdErr);
    }
  } else if (McuUtility_strncmp((char*)cmd, "McuTimeDate dateToSec ", sizeof("McuTimeDate dateToSec ")-1)==0) {
    *handled = TRUE;
    p = cmd+sizeof("McuTimeDate dateToSec ")-1;
    if (McuUtility_ScanDate(&p, &day, &month, &year)==ERR_OK) { /* format fine */
      if (McuUtility_ScanTime(&p, &hour, &minute, &second, &hsec)==ERR_OK) { /* format fine */
        time.Hour = hour;
        time.Min = minute;
        time.Sec = second;
#if McuTimeDate_HAS_SEC100_IN_TIMEREC
        time.Sec100 = 0;
#endif
        date.Day = day;
        date.Month = month;
        date.Year = year;
        unixSecs = McuTimeDate_TimeDateToUnixSeconds(&time, &date, 0);
        McuShell_SendNum32s(unixSecs, io->stdOut);
        McuShell_SendStr((uint8_t*)" sec\r\n", io->stdOut);
      } else {
        McuShell_SendStr((uint8_t*)"Wrong time format.\r\n", io->stdErr);
        return ERR_FAILED;
      }
    } else {
      McuShell_SendStr((uint8_t*)"Wrong date format.\r\n", io->stdErr);
      return ERR_FAILED;
    }
  } else if (McuUtility_strncmp((char*)cmd, "McuTimeDate date", sizeof("McuTimeDate date")-1)==0) {
    *handled = TRUE;
    return DateCmd(cmd, io);
  } else if (McuUtility_strncmp((char*)cmd, "McuTimeDate time", sizeof("McuTimeDate time")-1)==0) {
    *handled = TRUE;
    return TimeCmd(cmd, io);
  }
  return ERR_OK;
}

/*
** ===================================================================
**     Method      :  DeInit (component GenericTimeDate)
**
**     Description :
**         Deinitializes the driver.
**     Parameters  : None
**     Returns     : Nothing
** ===================================================================
*/
void McuTimeDate_DeInit(void)
{
  /* Nothing to do */
}

/*
** ===================================================================
**     Method      :  TicksToTime (component GenericTimeDate)
**
**     Description :
**         Transforms ticks into time information
**     Parameters  :
**         NAME            - DESCRIPTION
**         ticks           - 
**       * time            - Pointer where to store the time
**                           information
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuTimeDate_TicksToTime(uint32_t ticks, TIMEREC *time)
{
  time->Hour = (uint8_t)(ticks/(3600*McuTimeDate_TICKS_PER_S)); /* number of hours */
  ticks %= (3600*McuTimeDate_TICKS_PER_S); /* remainder of ticks inside hour */
  time->Min = (uint8_t)(ticks/(60*McuTimeDate_TICKS_PER_S)); /* number of minutes */
  ticks %= (60*McuTimeDate_TICKS_PER_S); /* remainder of ticks inside minute */
  time->Sec = (uint8_t)(ticks/McuTimeDate_TICKS_PER_S); /* number of seconds */
  ticks %= McuTimeDate_TICKS_PER_S;
#if McuTimeDate_HAS_SEC100_IN_TIMEREC
  time->Sec100 = (uint8_t)((ticks*(1000/McuTimeDate_TICKS_PER_S))/10); /* number of 1/100 seconds */
#endif
  return ERR_OK;
}

/*
** ===================================================================
**     Method      :  TimeToTicks (component GenericTimeDate)
**
**     Description :
**         Transforms time information into ticks
**     Parameters  :
**         NAME            - DESCRIPTION
**       * time            - Pointer where to time information
**       * ticks           - Pointer to where to store the ticks
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuTimeDate_TimeToTicks(TIMEREC *time, uint32_t *ticks)
{
  uint32_t cntr;

  /* Load given time re-calculated to McuTimeDate_TICK_TIME_MS ms ticks into software tick counter */
  cntr = (3600UL*McuTimeDate_TICKS_PER_S*(uint32_t)time->Hour)
              + (60UL*McuTimeDate_TICKS_PER_S*(uint32_t)time->Min)
              + (McuTimeDate_TICKS_PER_S*(uint32_t)time->Sec)
#if McuTimeDate_HAS_SEC100_IN_TIMEREC
              + ((McuTimeDate_TICKS_PER_S/100)*(uint32_t)time->Sec100)
#endif
              ;
  *ticks = cntr;
  return ERR_OK;
}

/*
** ===================================================================
**     Method      :  SetInternalRTCTimeDate (component GenericTimeDate)
**
**     Description :
**         This method sets a new actual time and date of the internal
**         hardware RTC.
**     Parameters  :
**         NAME            - DESCRIPTION
**       * time            - Pointer to struct which contains the
**                           time to be set. Can be NULL if time shall
**                           not be set.
**       * date            - Pointer to struct which contains the
**                           date information to be set. Can be NULL if
**                           date shall not be set.
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuTimeDate_SetInternalRTCTimeDate(TIMEREC *time, DATEREC *date)
{
#if McuTimeDate_CONFIG_USE_INTERNAL_HW_RTC_LDD
  LDD_RTC_TTime timeDate;

  /* get current time/date */
  RTC1_GetTime(RTC1_DeviceData, &timeDate); /* get information from HW RTC */
  if (time!=NULL) {
    timeDate.Hour = time->Hour;
    timeDate.Minute = time->Min;
    timeDate.Second = time->Sec;
  }
  if (date!=NULL) {
    timeDate.Year = date->Year;
    timeDate.Month = date->Month;
    timeDate.Day = date->Day;
    timeDate.DayOfWeek = McuTimeDate_CalculateDayOfWeek(date->Year, date->Month, date->Day);
  }
  return RTC1_SetTime(RTC1_DeviceData, &timeDate); /* get information from HW RTC */
#elif McuTimeDate_CONFIG_USE_INTERNAL_HW_RTC_BEAN
  uint8_t res;

  if (time!=NULL) {
    res = RTC1_SetTime(time->Hour, time->Min, time->Sec); /* set information in HW RTC */
    if (res!=ERR_OK) {
      return res;
    }
  }
  if (date!=NULL) {
    res = RTC1_SetDate(date); /* set information in HW RTC */
    if (res!=ERR_OK) {
      return res;
    }
  }
  return ERR_OK;
#else
  (void)time;
  (void)date;
  return ERR_FAILED; /* no HW RTC configured */
#endif
}

/*
** ===================================================================
**     Method      :  GetInternalRTCTimeDate (component GenericTimeDate)
**
**     Description :
**         This method returns current time and date from the hardware
**         RTC.
**     Parameters  :
**         NAME            - DESCRIPTION
**       * time            - Pointer to the structure to return the
**                           time. Can be NULL.
**       * date            - Pointer to structure which returns the
**                           date information. Can be NULL
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuTimeDate_GetInternalRTCTimeDate(TIMEREC *time, DATEREC *date)
{
#if McuTimeDate_CONFIG_USE_INTERNAL_HW_RTC_LDD
  LDD_RTC_TTime timeDate;

  RTC1_GetTime(RTC1_DeviceData, &timeDate); /* get information from HW RTC */
  if (time!=NULL) {
    time->Hour = timeDate.Hour;
    time->Min = timeDate.Minute;
    time->Sec = timeDate.Second;
#if McuTimeDate_HAS_SEC100_IN_TIMEREC
    time->Sec100 = 0;
#endif
  }
  if (date!=NULL) {
    date->Year = timeDate.Year;
    date->Month = timeDate.Month;
    date->Day = timeDate.Day;
  }
  return ERR_OK;
#elif McuTimeDate_CONFIG_USE_INTERNAL_HW_RTC_BEAN
  TIMEREC t;
  DATEREC d;
  uint8_t res;

  if (Time!=NULL) {
    res = RTC1_GetTime(&t); /* get information from HW RTC */
    if (res!=ERR_OK) {
      return res;
    }
    *time = t; /* struct copy */
  }
  if (date!=NULL) {
    res = RTC1_GetDate(&d); /* get information from HW RTC */
    if (res!=ERR_OK) {
      return res;
    }
    *date = d; /* struct copy */
  }
  return ERR_OK;
#else
  (void)time;
  (void)date;
  return ERR_FAILED; /* no HW RTC configured */
#endif
}

/*
** ===================================================================
**     Method      :  CalculateDayOfWeek (component GenericTimeDate)
**
**     Description :
**         Returns the day of the week, Sunday starting as zero. Monday
**         is 1, ...
**     Parameters  :
**         NAME            - DESCRIPTION
**         Year            - Years (16-bit unsigned integer)
**         Month           - Months (8-bit unsigned integer)
**         Day             - Days (8-bit unsigned integer)
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuTimeDate_CalculateDayOfWeek(uint16_t Year, uint8_t Month, uint8_t Day)
{
  /* see http://stackoverflow.com/questions/478694/what-is-the-easiest-algorithm-to-find-the-day-of-week-of-day-zero-of-a-given-yea */
  int32_t c, y, cc, yy, m, d;
  uint8_t dayOfWeek;

  cc = Year/100;
  yy = Year-((Year/100)*100);
  c = (cc/4) - 2*cc-1;
  y = 5*yy/4;
  m = 26*(Month+1)/10;
  d = Day;

  dayOfWeek = (uint8_t)(c+y+m+d)%7;
  return dayOfWeek; /* 0: Sunday, 1: Monday, ... 6: Saturday */
}

/*
** ===================================================================
**     Method      :  SyncSWtimeToInternalRTCsec (component GenericTimeDate)
**
**     Description :
**         This method synchronizes the software RTC with the internal
**         HW RTC. Because the internal RTC only counts seconds, we
**         sync on a second change.
**     Parameters  : None
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuTimeDate_SyncSWtimeToInternalRTCsec(void)
{
  /* This method synchronizes the software RTC with the internal HW RTC.
   * Because the internal RTC only counts seconds, we sync on a second change.
   */
  TIMEREC time;
  uint8_t secs;

  if (McuTimeDate_GetInternalRTCTimeDate(&time, NULL)!=ERR_OK) {
    return ERR_FAILED;
  }
  secs = time.Sec; /* remember current second counter */
  do {
    if (McuTimeDate_GetInternalRTCTimeDate(&time, NULL)!=ERR_OK) {
      return ERR_FAILED;
    }
  } while(secs==time.Sec);
  /* internal RTC has switched to the new second: sync internal RTC with it */
  if (McuTimeDate_SetSWTimeDate(&time, NULL)!=ERR_OK) { /* sync software RTC from HW RTC */
    return ERR_FAILED;
  }
  return ERR_OK;
}

/*
** ===================================================================
**     Method      :  SyncWithInternalRTC (component GenericTimeDate)
**
**     Description :
**         Synchronizes the software RTC with date and time from the
**         internal hardware RTC
**     Parameters  : None
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuTimeDate_SyncWithInternalRTC(void)
{
#if McuTimeDate_CONFIG_USE_INTERNAL_HW_RTC_LDD || McuTimeDate_CONFIG_USE_INTERNAL_HW_RTC_BEAN
  TIMEREC time;
  DATEREC date;
  uint8_t res;

  res = McuTimeDate_GetInternalRTCTimeDate(&time, &date);
  if (res!=ERR_OK) {
    return res;
  }
  /* update software time from hardware information */
#if McuTimeDate_HAS_SEC100_IN_TIMEREC
  res = McuTimeDate_SetTime(time.Hour, time.Min, time.Sec, time.Sec100);
#else
  res = McuTimeDate_SetTime(time.Hour, time.Min, time.Sec, 0);
#endif
  if (res!=ERR_OK) {
    return res;
  }
  res = McuTimeDate_SetDate(date.Year, date.Month, date.Day);
  if (res!=ERR_OK) {
    return res;
  }
  /* now sync to the second of the internal RTC */
  res = McuTimeDate_SyncSWtimeToInternalRTCsec();
  if (res!=ERR_OK) {
    return res;
  }
  return ERR_OK;
#else
  return ERR_FAILED; /* no hardware RTC available */
#endif
}

/*
** ===================================================================
**     Method      :  SyncWithExternalRTC (component GenericTimeDate)
**
**     Description :
**         Synchronizes the software RTC with date and time from the
**         hardware RTC. Note that if that RTC interface requires
**         interrupts, this function should be called only when
**         interrupts are enabled.
**     Parameters  : None
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuTimeDate_SyncWithExternalRTC(void)
{
#if McuTimeDate_CONFIG_USE_EXTERNAL_HW_RTC
  TIMEREC time;
  DATEREC date;
  uint8_t res;

  res = McuTimeDate_GetExternalRTCTimeDate(&time, &date);
  if (res!=ERR_OK) {
    return res;
  }
  /* update software time from hardware information */
#if McuTimeDate_HAS_SEC100_IN_TIMEREC
  res = McuTimeDate_SetTime(time.Hour, time.Min, time.Sec, time.Sec100);
#else
  res = McuTimeDate_SetTime(time.Hour, time.Min, time.Sec, 0);
#endif
  if (res!=ERR_OK) {
    return res;
  }
  return McuTimeDate_SetDate(date.Year, date.Month, date.Day);
#else
  return ERR_FAILED; /* no external RTC available */
#endif
}

/*
** ===================================================================
**     Method      :  SetExternalRTCTimeDate (component GenericTimeDate)
**
**     Description :
**         This method sets a new actual time and date of the external
**         hardware RTC.
**     Parameters  :
**         NAME            - DESCRIPTION
**       * time            - Pointer to struct which contains the
**                           time to be set. Can be NULL if time shall
**                           not be set.
**       * date            - Pointer to struct which contains the
**                           date information to be set. Can be NULL if
**                           date shall not be set.
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuTimeDate_SetExternalRTCTimeDate(TIMEREC *time, DATEREC *date)
{
#if McuTimeDate_CONFIG_USE_EXTERNAL_HW_RTC
  uint8_t res;

  if (time!=NULL) {
    res = McuExtRTC_SetTime(time->Hour, time->Min, time->Sec,
#if McuTimeDate_HAS_SEC100_IN_TIMEREC
        time->Sec100
#else
        0
#endif
    );
    if (res!=ERR_OK) {
      return res;
    }
  }
  if (date!=NULL) {
    res = McuExtRTC_SetDate(date->Year, date->Month, date->Day);
    if (res!=ERR_OK) {
      return res;
    }
  }
  return ERR_OK;
#else
  (void)time;
  (void)date;
  return ERR_FAILED; /* no external RTC available */
#endif
}

/*
** ===================================================================
**     Method      :  GetExternalRTCTimeDate (component GenericTimeDate)
**
**     Description :
**         This method returns current time and date from the external
**         hardware RTC.
**     Parameters  :
**         NAME            - DESCRIPTION
**       * time            - Pointer to the structure to return the
**                           time. Can be NULL.
**       * date            - Pointer to structure which returns the
**                           date information. Can be NULL
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuTimeDate_GetExternalRTCTimeDate(TIMEREC *time, DATEREC *date)
{
#if McuTimeDate_CONFIG_USE_EXTERNAL_HW_RTC
  uint8_t res;

  if (time!=NULL) {
    res = McuExtRTC_GetTime(time);
    if (res!=ERR_OK) {
      return res;
    }
  }
  if (date!=NULL) {
    res = McuExtRTC_GetDate(date);
    if (res!=ERR_OK) {
      return res;
    }
  }
  return ERR_OK;
#else
  (void)time;
  (void)date;
  return ERR_FAILED; /* no external RTC available */
#endif
}

/*
** ===================================================================
**     Method      :  SetTimeDate (component GenericTimeDate)
**
**     Description :
**         This method sets a new actual time and date of the RTC.
**     Parameters  :
**         NAME            - DESCRIPTION
**       * time            - Pointer to struct which contains the
**                           time to be set. Can be NULL if time shall
**                           not be set.
**       * date            - Pointer to struct which contains the
**                           date information to be set. Can be NULL if
**                           date shall not be set.
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuTimeDate_SetTimeDate(TIMEREC *time, DATEREC *date)
{
  uint8_t res = ERR_OK;
  bool failed = FALSE;

#if McuTimeDate_CONFIG_USE_SOFTWARE_RTC && McuTimeDate_CONFIG_SET_TIME_DATE_METHOD_USES_SOFTWARE_RTC
  res = McuTimeDate_SetSWTimeDate(time, date);
  if (res!=ERR_OK) {
    failed = TRUE;
  }
#endif
#if McuTimeDate_CONFIG_USE_INTERNAL_HW_RTC && McuTimeDate_CONFIG_SET_TIME_DATE_METHOD_USES_INTERNAL_RTC
  res = McuTimeDate_SetInternalRTCTimeDate(time, date);
  if (res!=ERR_OK) {
    failed = TRUE;
  }
#endif
#if McuTimeDate_CONFIG_USE_EXTERNAL_HW_RTC && McuTimeDate_CONFIG_SET_TIME_DATE_METHOD_USES_EXTERNAL_RTC
  res = McuTimeDate_SetExternalRTCTimeDate(time, date);
  if (res!=ERR_OK) {
    failed = TRUE;
  }
#endif
  if (failed) {
    res = ERR_FAILED;
  }
  return res;
}

/*
** ===================================================================
**     Method      :  GetTimeDate (component GenericTimeDate)
**
**     Description :
**         This method returns current time and date from the RTC.
**     Parameters  :
**         NAME            - DESCRIPTION
**       * time            - Pointer to the structure to return the
**                           time. Can be NULL.
**       * date            - Pointer to structure which returns the
**                           date information. Can be NULL
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuTimeDate_GetTimeDate(TIMEREC *time, DATEREC *date)
{
  uint8_t res;

#if McuTimeDate_CONFIG_USE_SOFTWARE_RTC && McuTimeDate_CONFIG_USE_GET_TIME_DATE_METHOD==McuTimeDate_GET_TIME_DATE_METHOD_SOFTWARE_RTC
  res = McuTimeDate_GetSWTimeDate(time, date);
#elif McuTimeDate_CONFIG_USE_INTERNAL_HW_RTC && McuTimeDate_CONFIG_USE_GET_TIME_DATE_METHOD==McuTimeDate_GET_TIME_DATE_METHOD_INTERNAL_RTC
  res = McuTimeDate_GetInternalRTCTimeDate(time, date);
#elif McuTimeDate_CONFIG_USE_EXTERNAL_HW_RTC && McuTimeDate_CONFIG_USE_GET_TIME_DATE_METHOD==McuTimeDate_GET_TIME_DATE_METHOD_EXTERNAL_RTC
  res = McuTimeDate_GetExternalRTCTimeDate(time, date);
#else
  #error "invalid configuration!"
  res = ERR_FAILED;
#endif
  return res;
}

/*
** ===================================================================
**     Method      :  UnixSecondsToTimeDateCustom (component GenericTimeDate)
**
**     Description :
**         Transforms a given time/date into the Unix time stamp, with
**         the number of seconds from a starting date
**     Parameters  :
**         NAME            - DESCRIPTION
**         seconds         - Unix time stamp in seconds after
**                           the given base year and base month
**         offset_hours    - Optional time zone
**                           offset, use 0 for no offset
**       * time            - Pointer to TIMEREC struct where the
**                           result is stored. Can be NULL.
**       * date            - Pointer to DATEREC struct where the
**                           result is stored. Can be NULL.
**         baseYear        - Base year from which is used as
**                           base to count the seconds, in YYYY format.
**     Returns     : Nothing
** ===================================================================
*/
void McuTimeDate_UnixSecondsToTimeDateCustom(int32_t seconds, int8_t offset_hours, TIMEREC *time, DATEREC *date, uint16_t baseYear)
{
  /* Implementation based on https://www.experts-exchange.com/questions/26868405/convert-unix-time-stamp-for-8-bit-C-compiler.html */
  uint8_t daysmonth[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  int32_t secs_per_year;
  int32_t secs_per_month;
  uint16_t year;
  uint8_t month;
  uint8_t day;
  uint8_t hours;
  uint8_t minutes;

  /* adjust seconds to localtime */
  seconds -= (int32_t)offset_hours*3600;
  /* year */
  year = baseYear;
  if (McuUtility_IsLeapYear(year)) {
    secs_per_year = (365+1)*24*3600;
  } else {
    secs_per_year = 365*24*3600;
  }
  while (seconds>=secs_per_year) {
    seconds -= secs_per_year;
    year++;
    if (McuUtility_IsLeapYear(year)) {
      secs_per_year = (365+1)*24*3600;
    } else {
      secs_per_year = 365*24*3600;
    }
  }
  /* month update based on leap year */
  if (McuUtility_IsLeapYear(year)) { /* adjust leap year month days */
    daysmonth[2] = 29;
  } else {
    daysmonth[2] = 28;
  }
  /* month */
  month = 1;
  secs_per_month = (int32_t)daysmonth[1]*24*3600;
  while (seconds >= secs_per_month) {
    seconds -= secs_per_month;
    month++;
    secs_per_month = (int32_t)daysmonth[month]*24*3600;
  }
  /* day */
  day = (uint8_t)(seconds/(24*3600))+1;
  seconds = seconds%(24*3600);

  hours   = (uint8_t)(seconds/3600);
  seconds = seconds%3600;

  minutes = (uint8_t)(seconds/60);
  seconds = seconds%60;

  if (date!=NULL) {
    date->Day = day;
    date->Month = month;
    date->Year = year;
  }
  if (time!=NULL) {
    time->Hour = hours;
    time->Min = minutes;
    time->Sec = (uint8_t)seconds;
#if McuTimeDate_HAS_SEC100_IN_TIMEREC
    time->Sec100 = 0;
#endif
  }
}

/*
** ===================================================================
**     Method      :  TimeDateToUnixSecondsCustom (component GenericTimeDate)
**
**     Description :
**         Returns for a given time/date the corresponding UNIX time
**         stamp with a custom base date.
**     Parameters  :
**         NAME            - DESCRIPTION
**       * time            - Pointer to TIMEREC which holds the time
**                           to convert
**       * date            - Pointer to DATEREC with the date to
**                           convert
**         offset_hours    - Optional number of
**                           offset hours to adjust for the time zone.
**                           Use 0 for no adjustment.
**         baseYear        - Base year from which is used as
**                           base to count the seconds, in YYYY format
**     Returns     :
**         ---             - number of seconds after given base
**                           month/year
** ===================================================================
*/
int32_t McuTimeDate_TimeDateToUnixSecondsCustom(TIMEREC *time, DATEREC *date, int8_t offset_hours, uint16_t baseYear)
{
  /* implementation based on https://www.experts-exchange.com/questions/26868405/convert-unix-time-stamp-for-8-bit-C-compiler.html */
  uint8_t daysmonth[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  int32_t nofSecs;
  uint16_t year;
  uint8_t month;

  /* calculate the seconds for the given day */
  nofSecs = (int32_t)time->Sec;
  nofSecs += ((int32_t)time->Min)*60;
  nofSecs += (int32_t)time->Hour*3600;
  nofSecs += (int32_t)offset_hours*3600;
  nofSecs += (int32_t)(date->Day-1)*24*3600;
  /* month update based on leap year */
  if (McuUtility_IsLeapYear(date->Year)) { /* adjust leap year month days */
    daysmonth[2] = 29;
  } else {
    daysmonth[2] = 28;
  }
  /* calculate the seconds for the months in the given date */
  for (month = date->Month-1; month>0; month--) {
    nofSecs += (int32_t)daysmonth[month]*24*3600;
  }
  /* count the years back to the base year */
  for (year=date->Year-1; year>=baseYear; year--) {
    if (McuUtility_IsLeapYear(year)) {
      nofSecs += (365+1)*24*3600;
    } else {
      nofSecs += 365*24*3600;
    }
  }
  return nofSecs;
}

/*
** ===================================================================
**     Method      :  UnixSecondsToTimeDate (component GenericTimeDate)
**
**     Description :
**         Transforms a given time/date into the Unix time stamp, with
**         the number of seconds after 1-Jan-1970
**     Parameters  :
**         NAME            - DESCRIPTION
**         seconds         - Unix time stamp in seconds after
**                           the given base year and base month
**         offset_hours    - Optional time zone
**                           offset, use 0 for no offset
**       * time            - Pointer to TIMEREC struct where the
**                           result is stored. Can be NULL.
**       * date            - Pointer to DATEREC struct where the
**                           result is stored. Can be NULL.
**     Returns     : Nothing
** ===================================================================
*/
void McuTimeDate_UnixSecondsToTimeDate(int32_t seconds, int8_t offset_hours, TIMEREC *time, DATEREC *date)
{
  McuTimeDate_UnixSecondsToTimeDateCustom(seconds, offset_hours, time, date, 1970);
}

/*
** ===================================================================
**     Method      :  TimeDateToUnixSeconds (component GenericTimeDate)
**
**     Description :
**         Returns for a given time/date the corresponding UNIX time
**         stamp, starting at 1-Jan-1970.
**     Parameters  :
**         NAME            - DESCRIPTION
**       * time            - Pointer to TIMEREC which holds the time
**                           to convert
**       * date            - Pointer to DATEREC with the date to
**                           convert
**         offset_hours    - Optional number of
**                           offset hours to adjust for the time zone.
**                           Use 0 for no adjustment.
**     Returns     :
**         ---             - number of seconds after given base
**                           month/year
** ===================================================================
*/
int32_t McuTimeDate_TimeDateToUnixSeconds(TIMEREC *time, DATEREC *date, int8_t offset_hours)
{
  return McuTimeDate_TimeDateToUnixSecondsCustom(time, date, offset_hours, 1970);
}

/*
** ===================================================================
**     Method      :  AddDateString (component GenericTimeDate)
**
**     Description :
**         Adds a formatted date string to a buffer
**     Parameters  :
**         NAME            - DESCRIPTION
**       * buf             - Pointer to zero terminated buffer where to
**                           append the string
**         bufSize         - Size of the buffer in bytes
**       * date            - Pointer to date information
**       * format          - Pointer to the format string.
**                           Supported formats: "dd.mm.yyyy"
**     Returns     :
**         ---             - Error code, ERR_OK for no error
** ===================================================================
*/
uint8_t McuTimeDate_AddDateString(uint8_t *buf, size_t bufSize, DATEREC *date, uint8_t *format)
{
  /* currently only the following format is supported: "dd.mm.yyyy" */
  (void)format; /* not supported yet */
  McuUtility_strcatNum16uFormatted(buf, bufSize, date->Day, '0', 2);
  McuUtility_chcat(buf, bufSize, '.');
  McuUtility_strcatNum16uFormatted(buf, bufSize, date->Month, '0', 2);
  McuUtility_chcat(buf, bufSize, '.');
  McuUtility_strcatNum16u(buf, bufSize, (uint16_t)date->Year);
  return ERR_OK;
}

/*
** ===================================================================
**     Method      :  AddTimeString (component GenericTimeDate)
**
**     Description :
**         Adds a formatted time string to a buffer
**     Parameters  :
**         NAME            - DESCRIPTION
**       * buf             - Pointer to zero terminated buffer where to
**                           append the string
**         bufSize         - Size of the buffer in bytes
**       * time            - Pointer to time information
**       * format          - Pointer to the format string.
**                           Supported formats: "hh:mm.ss,cc"
**     Returns     :
**         ---             - Error code, ERR_OK for no error
** ===================================================================
*/
uint8_t McuTimeDate_AddTimeString(uint8_t *buf, size_t bufSize, TIMEREC *time, uint8_t *format)
{
  /* currently only the following format is supported: hh:mm:ss,cc" */
  (void)format; /* not supported yet */
  McuUtility_strcatNum16sFormatted(buf, bufSize, time->Hour, '0', 2);
  McuUtility_chcat(buf, bufSize, ':');
  McuUtility_strcatNum16sFormatted(buf, bufSize, time->Min, '0', 2);
  McuUtility_chcat(buf, bufSize, ':');
  McuUtility_strcatNum16sFormatted(buf, bufSize, time->Sec, '0', 2);
  McuUtility_chcat(buf, bufSize, ',');
#if McuTimeDate_HAS_SEC100_IN_TIMEREC
  McuUtility_strcatNum16sFormatted(buf, bufSize, time->Sec100, '0', 2);
#else
  McuUtility_strcatNum16sFormatted(buf, bufSize, 0, '0', 2);
#endif
  return ERR_OK;
}

/* END McuTimeDate. */

/*!
** @}
*/
