GPS time and calculations

Status
Not open for further replies.

Manu

Well-known member
Hello,

In a project I use a GPS and need to make some calculations based on time and days of the week. I use an UBlox GPS and currently sync the Teensy RTC with the GPS with this :

Code:
bool isDaylightSavingTime()
{
  tmElements_t te;

  // Date pour le 1er Mars
  te.Year = year() - 1970;
  te.Month = 3;
  te.Day = 1;
  te.Hour = 0;
  te.Minute = 0;
  te.Second = 0;

  time_t dstStart, dstEnd;

  // Date pour le troisième dimanche du mois de mars à 2H AM
  dstStart = makeTime(te);
  dstStart = nextSunday(nextSunday(nextSunday(dstStart)));
  dstStart += 2 * SECS_PER_HOUR;

  // Date pour le premier Dimanche de Novembre à 1H AM
  te.Month = 11;
  dstEnd = makeTime(te);
  dstEnd = nextSunday(dstEnd);
  dstEnd += SECS_PER_HOUR;

  // Si l'heure d'été est active, alors la fonction renvoie "true"
  return (now() >= dstStart && now() < dstEnd);
}

Code:
void syncTimeWithGps()
{
  setTime(uBloxData.utcHour, uBloxData.utcMin, uBloxData.utcSec, uBloxData.utcDay, uBloxData.utcMonth, uBloxData.utcYear - 2000);

  int32_t secondsToAdjust = timeZone * SECS_PER_HOUR; // Adjust timeZone
  if (isDaylightSavingTime())
  {
    secondsToAdjust += SECS_PER_HOUR;
  }
  adjustTime(secondsToAdjust);
  Teensy3Clock.set(now());
}


isDayLightSaving() is a function that calculate if the code need to ad an hour to current time in regard with "day light saving".
syncTimeWithGps() is a function that set the teensy RTC with GPS time as soon as GPS have a fix. This is required to determine/use isDayLightSaving(). It sync RTC with CURRENT time, not UTC. I mean it take in account the timezone and day light saving offset. UTC time is still available from GPS, at any time.

Now, based on the RTC or GPS (preferably GPS) I have to determine :
- The week of the current year
- The elapsed seconds since the beginning of the current week (start at midnight GMT on Saturday night)

Using "TimeLib.h", I can't figure how to do this. Can someone point me to the right direction ?
I'm really useless with date calculations...

Thank you,
Manu
 
Last edited:
I think I have a function for elapsed seconds since the beginning of the current week (start at midnight GMT on Saturday night) :

Code:
uint32_t elapsedSecondsSinceSundayZero() // With TimeZone and DayLight Adjustment
{
  tmElements_t te;
  te.Year = uBloxData.utcYear - 2000;
  te.Month = uBloxData.utcMonth;
  te.Day = uBloxData.utcDay;
  te.Hour = uBloxData.utcHour + timeZone;
  te.Minute = uBloxData.utcMin;
  te.Second = uBloxData.utcSec;

  if (isDaylightSavingTime())
  {
    te.Hour += 1;
  }

  time_t dayNow;
  dayNow = makeTime(te);

  uint32_t elapsedSeconds = elapsedSecsThisWeek(dayNow);
  return elapsedSeconds;
}

But it return only elapsed seconds of THIS day :-(

So I find that elapsedSecsToday and elapsedSecsThisWeek return the same numbers...

dayOfWeek(dayNow) return 1
 
Last edited:
The function with some debug :

Code:
uint32_t elapsedSecondsSinceSundayZero() // With TimeZone and DayLight Adjustment
{
  tmElements_t te;

  /*
  te.Year = 21;
  te.Month = 3;
  te.Day = 23;
  te.Hour = 18;
  te.Minute = 30;
  te.Second = 0;
  */

  te.Year = uBloxData.utcYear - 2000;
  te.Month = uBloxData.utcMonth;
  te.Day = uBloxData.utcDay;
  te.Hour = uBloxData.utcHour + timeShiftRelatedToZone;
  te.Minute = uBloxData.utcMin;
  te.Second = uBloxData.utcSec;

  if (isDaylightSavingTime())
  {
    te.Hour += 1;
  }

  Serial.print(te.Hour);
  Serial.print(":");
  Serial.print(te.Minute);
  Serial.print(":");
  Serial.print(te.Second);
  Serial.print(" - ");
  Serial.print(te.Day);
  Serial.print("/");
  Serial.print(te.Month);
  Serial.print("/");
  Serial.print(te.Year);
  Serial.print(" => ");

  time_t dayNow;
  dayNow = makeTime(te);
  Serial.print("Day Of The Week : ");
  Serial.print(dayOfWeek(dayNow));
  Serial.print(" => function return ");
  //uint32_t elapsedSecondsThisWeek = elapsedSecsToday(dayNow);
  uint32_t elapsedSecondsThisWeek = elapsedSecsThisWeek(dayNow);
  Serial.print(elapsedSecondsThisWeek);
  Serial.print(" seconds = ");
  Serial.print(elapsedSecondsThisWeek / SECS_PER_HOUR);
  Serial.println(" hours");
  return elapsedSecondsThisWeek;
}

And the output :

Code:
18:46:12 - 17/3/21 => Day Of The Week : 1 => function return 67572 seconds = 18 hours
18:46:13 - 17/3/21 => Day Of The Week : 1 => function return 67573 seconds = 18 hours
18:46:14 - 17/3/21 => Day Of The Week : 1 => function return 67574 seconds = 18 hours
18:46:15 - 17/3/21 => Day Of The Week : 1 => function return 67575 seconds = 18 hours
18:46:16 - 17/3/21 => Day Of The Week : 1 => function return 67576 seconds = 18 hours
18:46:17 - 17/3/21 => Day Of The Week : 1 => function return 67577 seconds = 18 hours
18:46:18 - 17/3/21 => Day Of The Week : 1 => function return 67578 seconds = 18 hours

dayOfWeek(dayNow) seem to not operate as expected
 
Last edited:
With the function above, if I set date to 23/3/21, the dayOfWeek is 7. It should be 2.

Ah, I forgot to say that I use Teensy 3.2 and this is with TimeLib.h Library provided by Paul.

Code:
18:30:0 - 23/3/21 => Day Of The Week : 7 => function return 585000 seconds = 162 hours
18:30:0 - 23/3/21 => Day Of The Week : 7 => function return 585000 seconds = 162 hours
18:30:0 - 23/3/21 => Day Of The Week : 7 => function return 585000 seconds = 162 hours
18:30:0 - 23/3/21 => Day Of The Week : 7 => function return 585000 seconds = 162 hours
 
Here is a simple code that demonstrate that DayOfTheWeek does not operate :

Code:
#include <TimeLib.h>

void setup()  {
  Serial.begin(115200);
  while ((!Serial) && (millis() < 500));

  tmElements_t te;

  for (int i = 14; i < 21; i++) { //03-14-2021 = Sunday
    te.Year = 21;
    te.Month = 3;
    te.Day = i;
    te.Hour = 18;
    te.Minute = 30;
    te.Second = 0;

    Serial.print(te.Hour);
    Serial.print(":");
    Serial.print(te.Minute);
    Serial.print(":");
    Serial.print(te.Second);
    Serial.print(" - ");
    Serial.print(te.Day);
    Serial.print("/");
    Serial.print(te.Month);
    Serial.print("/");
    Serial.print(te.Year);
    Serial.print(" => ");

    time_t CurrentDay;
    CurrentDay = makeTime(te);

    Serial.print("Day Of The Week : ");
    Serial.print(dayOfWeek(CurrentDay));
    Serial.print(" => function return ");
    //uint32_t elapsedSecondsThisWeek = elapsedSecsToday(CurrentDay);
    uint32_t elapsedSecondsThisWeek = elapsedSecsThisWeek(CurrentDay);
    Serial.print(elapsedSecondsThisWeek);
    Serial.print(" seconds = ");
    Serial.print(elapsedSecondsThisWeek / SECS_PER_HOUR);
    Serial.println(" hours");
  }
}

void loop() {

}

Here the output :

Code:
18:30:0 - 14/3/21 => Day Of The Week : 5 => function return 412200 seconds = 114 hours
18:30:0 - 15/3/21 => Day Of The Week : 6 => function return 498600 seconds = 138 hours
18:30:0 - 16/3/21 => Day Of The Week : 7 => function return 585000 seconds = 162 hours
18:30:0 - 17/3/21 => Day Of The Week : 1 => function return 66600 seconds = 18 hours
18:30:0 - 18/3/21 => Day Of The Week : 2 => function return 153000 seconds = 42 hours
18:30:0 - 19/3/21 => Day Of The Week : 3 => function return 239400 seconds = 66 hours
18:30:0 - 20/3/21 => Day Of The Week : 4 => function return 325800 seconds = 90 hours
 
Seems the formular for leap years is wrong.

Edit: No.. must be an other issue. sry
 
Last edited:
The problem is that timelib uses 1970 as the epoch and the year is offset by that amount. To set the year properly you would have to so this:
Code:
    te.Year = 2021-1970;

and to print the last two digits you would do this:
Code:
    Serial.print(te.Year+1970-2000);

Pete
 
Thank you Pete,

This was exactly my problem (and fault).
Any help to calculate "WeekOfTheYear" ?

I have an idea but need some time to make it real (Busy today). I'll use and count "nextsunday" from 1/1/currentYear until I reach the current week.
 
I have a bit of code to calculate the week (and day) number. First calculate the time_t value of the start of the year - only need to do this once:
Code:
// Need to adjust for years after 2021
  tmElements_t te;
  te.Year = 2021-1970;
  te.Month = 1;
  te.Day = 1;
  te.Hour = 0;
  te.Minute = 0;
  te.Second = 0;
  time_t StartOfYear = makeTime(te);
From that we can calculate the number of seconds elapsed this year which then allows us to calculate the number of days and weeks that have elapsed.
Code:
    // Figure out the day and week number
    time_t SecsThisYear = CurrentDay - StartOfYear;
    // Jan 1st is day one
    int DaysThisYear = SecsThisYear / SECS_PER_DAY + 1;
    Serial.printf(" : days = %d, weeks = %d\n",DaysThisYear,(DaysThisYear-1)/7+1);

Pete
 
Thank you Pete. I used another way while trying to figure if the first week of the year is not a full 7 days week :

Code:
uint16_t findWeekOfTheYear()
{
  int16_t currentWeek = 1;
  tmElements_t te;
  time_t dateStart, dateCurrent;

  // Date pour le 1er Janvier
  te.Year = uBloxData.utcYear - 1970;
  te.Month = 1;
  te.Day = 1;
  te.Hour = 0;
  te.Minute = 1;
  te.Second = 0;
  dateStart = makeTime(te);

  // Date actuelle
  te.Year = uBloxData.utcYear - 1970;
  te.Month = uBloxData.utcMonth;
  te.Day = uBloxData.utcDay;
  te.Hour = uBloxData.utcHour + timeShiftRelatedToTimeZone;
  te.Minute = uBloxData.utcMin;
  te.Second = uBloxData.utcSec;
  dateCurrent = makeTime(te);
  while (dateStart <= dateCurrent)
  {
    time_t deltaDate;
    deltaDate = nextSunday(dateStart);
    deltaDate = deltaDate - dateStart;
    dateStart += deltaDate;
    if ((dateStart + SECS_PER_WEEK) < dateCurrent)
      currentWeek += 1;
  }
  return currentWeek;
}

But I'm still stuck because of years with 53 weeks
 
I tried it, but as I expected, it don't take in account that the first week of the year often have less than 7 days. So today your code return week 12 while we are on week 11.

And there is one rule : It says Week 1 of a year is the one which contains at least 4 days from the given year. (come from ISO 8601 that defines a standard for the representation of dates, times and time zones).

https://en.wikipedia.org/wiki/ISO_8601#Week_dates

With standard C++ time library there is this :
https://www.cplusplus.com/reference/ctime/strftime/
 
Last edited:
Yes, the U.S. is cooking up a special soup for many things. I wonder what the advantage is to do everything differently than everyone else. Must be money$$$ ;)
 
I didn't know that there was a way of counting the weeks that can put the first few days of January "in week 52 or 53 of the previous year" (from ISO-8601).
I'll try to mangle the code to sort that out.

Pete
 
Here's some code which follows the wikipedia article on the subject.
Code:
int p(int yr)
{
  return (yr +yr/4 - yr/100 + yr/400)%7;
}

int weeks(int yr)
{
  if(p(yr) == 4)return(53);
  if(p(yr-1) == 3)return(53);
  return 52;  
}

int ISO_week_number(time_t current_time)
{
  // Figure out the ISO-8601 week number where
  // week number one is the week containing
  // January 4th, or equivalently the week
  // containing the first Thursday of the year
  // See: https://en.wikipedia.org/wiki/ISO_week_date

// Get time_t of the first day of the year
// Convert current_time to a tmElements_t struct.
  tmElements_t te;
  breakTime(current_time,te);
  // Overwrite all except the year to get Jan 1 of this year
  te.Month = 1;
  te.Day = 1;
  te.Hour = 0;
  te.Minute = 0;
  te.Second = 0;
  // and convert it back to a time_t
  time_t StartOfYear = makeTime(te);

  // Now convert it back to tmElements_t struct to
  // get the day of the week .Wday for Jan 1st
  breakTime(StartOfYear,te);
 
  time_t SecsThisYear = current_time - StartOfYear;
  // Jan 1st is day one
  int DaysThisYear = SecsThisYear / SECS_PER_DAY + 1;

  int week_of_year = (10 + DaysThisYear - te.Wday)/7;

  int this_year = te.Year + 1970;
  if(week_of_year < 1)week_of_year = weeks(this_year - 1);
  else if(week_of_year > weeks(this_year)) week_of_year = 1;

  return week_of_year;
}

Pete
 
Hello Pete,

Sorry for the long delay, I was busy up to now.
Thank you for the code, I'll try it tomorrow and let you know how it behave. I had a quick look on it and, as I state in the first post, I'm really useless with date calculus... I don't understand what you do. I'll have a deeper look tomorrow.

Anyway, thank you.
Manu
 
Hi Pete,

Today I dig in the code because it didn't operate correctly, the week of the year was always week+1. I found that te.Wday will always return 0. So the week calculation would never take current day in account. Here is the corrected code :

Code:
// find if year is a Leap Year
int leap(int yr)
{
  return (yr + yr / 4 - yr / 100 + yr / 400) % 7;
}

// Find the number of week in the current Year
int weeks(int yr)
{
  if (leap(yr) == 4)
    return (53);
  if (leap(yr - 1) == 3)
    return (53);
  return 52;
}

// Find the current week of the year
uint16_t findWeekOfTheYear()
{
  tmElements_t te;
  time_t startOfYear, dateCurrent;

  // Date pour le 1er Janvier
  te.Year = uBloxData.utcYear - 1970;
  te.Month = 1;
  te.Day = 1;
  te.Hour = 0;
  te.Minute = 0;
  te.Second = 0;
  startOfYear = makeTime(te);

  // Current Date
  te.Year = uBloxData.utcYear - 1970;
  te.Month = uBloxData.utcMonth;
  te.Day = uBloxData.utcDay;
  te.Hour = uBloxData.utcHour + timeShiftRelatedToTimeZone;
  te.Minute = uBloxData.utcMin;
  te.Second = uBloxData.utcSec;
  dateCurrent = makeTime(te);

  time_t SecsThisYear = dateCurrent - startOfYear;
  int DaysThisYear = SecsThisYear / SECS_PER_DAY + 1;
  int week_of_year = (10 + DaysThisYear - [B]dayOfWeek(dateCurrent)[/B]) / 7;
  int this_year = te.Year + 1970;
  if (week_of_year < 1)
    week_of_year = weeks(this_year - 1);
  else if (week_of_year > weeks(this_year))
    week_of_year = 1;
  return week_of_year;
}

Again, thank you.
Manu
 
Status
Not open for further replies.
Back
Top