My attempt at detecting EST/EDT change automatically

Thanks, I’ll check out your example.

@bko, nice code…very clean.

Slightly optimized code…version 2 below:

//
//  Last update:  03/11/18
//

typedef struct DSTStruct
{
    int year;
    int startDay;                                               // DST start day in Mar
    int endDay;                                                 // DST end day in Nov
} DST;

DST dst[] = {                                                   // Table data for Richmond, VA
    12,0,0,                                                     // dst[0].year field holds # of elements
    2018,11,4,
    2019,10,3,
    2020,8,1,
    2021,14,7,
    2022,13,6,
    2023,12,5,
    2024,10,3,
    2025,9,2,
    2026,8,1,
    2027,14,7,
    2028,12,5,
    2029,11,4
};

void setup(void) 
{
    int rc;
    
    Serial.begin(115200);
    Time.zone(-5);                                              // EST/EDT
    rc = isDSTActive();
    if (rc == 0)
    {
        Time.endDST();    
        Serial.println("DST not active");
    }
    else if (rc == 1)
    {
        Time.setDSTOffset(1.0);                                 // Offset is 1 hour
        Time.beginDST();        
        Serial.println("DST active");
    }
    else Serial.println("Error determining DST");
}

void loop(void) 
{
    Serial.println(Time.format("%r"));
    delay(1000);
}

int isDSTActive(void)
{
    int sDay,                                                   // Start day in Mar    
        eDay;                                                   // End day in Nov

    if (Time.month() > 3 && Time.month() < 11)                  // Apr - Oct = EDT
        return 1;
    else if (Time.month() < 3 || Time.month() == 12)            // Jan, Feb, Dec = EST
        return 0;

    if (!findYear(Time.year(), &sDay, &eDay))                   // Did we find this year's data?
    {
        Serial.println("Error - couldn't find data for this year");
        return 2;
    }

    switch (Time.month())
    {
        case 3:                                                 // March (spring forward)
            if ((Time.day() > sDay) || (Time.day() == sDay && Time.hour() > 1))
                return 1;
            else 
                return 0;
        case 11:                                                // November (fall back)
            if ((Time.day() < eDay) || (Time.day() == eDay && Time.hour() < 2))
                return 1;
            else 
                return 0;
    }
}

bool findYear(int year, int *sDay, int *eDay)
{
    int numItems = dst[0].year;                                 // How many structure items?
    
    for (int x = 1; x <= numItems; x++)
    {
        if (dst[x].year == year)
        {
            *sDay = dst[x].startDay;
            *eDay = dst[x].endDay;
            return true;                                        // Found current year's data
        }
    }
    return false;
}

Your code seems a bit more complicated than necessary. I’ve been using a slightly modified version of code I saw here on the forum. I keep it in a Helpers file where I keep often used functions. The sevens and eights in the code are specific for my west coast location.

void setZone() {
	int month = Time.month();
	int day = Time.day();
	int weekday = Time.weekday();
	int previousSunday = day - weekday + 1;

	if (month < 3 || month > 11) {
		Time.zone(-8);
	}else if (month > 3 && month < 11) {
		Time.zone(-7);
	}else if (month == 3) {
		int offset = (previousSunday >= 8)? -7 : -8;
		Time.zone(offset);
	}else{
		int offset = (previousSunday <= 0)? -7 : -8;
		Time.zone(offset);
	}
}

I call this function once a day (at 3 AM) when I also do a time sync with the particle cloud.

4 Likes

Thanks to @bko and @ric for their suggestions and code samples…here is my hopefully last update to this code. I wanted a standalone chunk of code that didn’t depend on or set the timezone but rather just tell me whether DST was in effect. I handle the timezone setting and the DST offset handling elsewhere.

#pragma once
//
//  Last update:  03/13/18
//
/****************************************************/
/*  isDSTActive                                     */
/*  Output:                                         */
/*      false = DST not in effect                   */
/*      true = DST in effect                        */
/****************************************************/
bool isDSTActive(void)
{
    int month = Time.month(),
        day = Time.day(),
        weekday = Time.weekday(),
        previousSunday = day - weekday + 1;

    if (month > 3 && month < 11)                                    // Apr - Oct = EDT
        return true;
    else if (month < 3 || month == 12)                              // Jan, Feb, Dec = EST
        return false;

    switch (month)
    {
        case 3:                                                     // March (spring forward)
            if (previousSunday >= 8)
                return true;
            else
                return false;
        case 11:                                                    // November (fall back)
            if (previousSunday <= 0)
                return true;
            else
                return false;
    }
}

I should also say that I run this code once a day at 2am since that is when such changes occur in my timezone.
2 Likes

Did this code perform well on Sunday @syrinxtech? I will need to implement something tonight! :wink:
Thanks

I think it is easier to use a simple function:

called like this at the start of loop():

void loop(void) {
  Time.zone(IsDST(Time.day(), Time.month(), Time.weekday()) ? summerOffset : winterOffset);
  // etc...

for europe:

bool IsDst(int day, int month, int dayOfWeek)
{
  if (month < 3 || month > 10)
  {
    return false;
  }
  if (month > 3 && month < 10)
  {
    return true;
  }
  int previousSunday = day - dayOfWeek;
  if (month == 3)
  {
    return previousSunday >= 25;
  }
  if (month == 10) return previousSunday < 25;
  {
    return false;
  }
}
2 Likes

Working like a charm @daneboomer.

@BulldogLowell, I wouldn’t want this in my loop() code because it will be called many more times than needed, at least for my location. In EST, DST only changes two times a year at two AM. Calling this function repeatedly seems like a huge waste of processor.

I only call my function once a day at 2am and it seems to work very well.

@syrinxtech, the overhead of testing for a 2am condition is not much different than calling the function more often. However, in terms of program logic, your approach is clean.

1 Like

the function's overhead is trivial, but I do get your point about running once a day.

the function I provided is just a way easier to understand than your examples, in my opinion and was directed at @daneboomer

Of course there are an uncountable number of ways to do this. I'm waiting for a Particle implementation of some DST adjustment, which looks like it may have been once intended but never implemented.

3 Likes

One other reason for running the isDST() check not only at the exepcted switching time is that your device might not exactly be running there and then, so you should at least run it also after a reset or any wake from sleep.

1 Like

@BulldogLowell, agreed…this is definitely an area that I feel needs to be settled once and for all by Particle. Every major OS can handle this issue. And yes, I realize that RTOS is not a full-fledged OS, but with all of the good examples put forth by the members of this group, something should be able to be put together that works for all time zones and daylight savings time participants, everywhere in the world.

@ScruffR, great point.

In my code I always do this check on boot and everyday at 2am.

1 Like

...like abandoning Daylight Savings Time completely, as it should be. :wink:

2 Likes

agreed…but I don’t Particle could pull that off alone.

It turns out, (I think! :smiley: :smiley: ) that it's not just the sevens and eights that are specific it's the '11' too? Because the changeover day for end of DST is different in Europe too! Egg on my face. It ended last Sunday. Not sure what I'd need to change to make it work for Europe. Sorry. Not a coder!

@daneboomer, you have a similar question in another thread where you already found my implementation which works for Europe.

Double-posting is rarely helpful.

Thanks @ScruffR. I think this is one of those exceptional cases where it might be helpful. But I await schooling on why it isn’t :wink:

This post here alerts people who might be reading THIS thread that the @Ric code isn’t suitable as is for the European changeover dates.

While it might help people who are unaware of the fact that different continents use different switching dates to learn about that, it’s not really helpful to ask for a solution in multiple places because it will consume time on multiple fronts to get virtually the same answer.

Hence it is usually better practice to provide links to the one place where the specific discussion can/should be continued instead of scattering answers all over the forum.

1 Like

Schooled. Thanks! :slight_smile: I do appreciate you, @ScruffR!

1 Like