Confusion with Time.h and TimeLib.h!

Hi,

The arduino learning playground is somewhat incomplete, as you can see in this page:

First it doesn't says the simple thing of how import the Library, wich one of these is the right way?

  • <Time.h>
  • <TimeLib.h>

With this kind of references you need to try it!!!!! :fearful: So I did it:

#include <Time.h>

void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);

}

void loop() {
  // print out seconds:
  Serial.println(second());

  delay (100);
}
#include <TimeLib.h>

void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);

}

void loop() {
  // print out seconds:
  Serial.println(second());

  delay (100);
}

Guess what, the first code gives error and the second not... :stuck_out_tongue_closed_eyes:

Bottom line, Time.h is useless.

What do you need to do? maybe the standard <time.h> std C library will be enough.

1 Like

KeithRB:
What do you need to do? maybe the standard <time.h> std C library will be enough.

I wan't to use an alternative to delay() so that the loop functions doesn't stop. This may be done ONLY with <TimeLib.h> this way:

#include <TimeLib.h>

// start now time variable in seconds
int time_now = now();
  
void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);

  // initialize digital pin LED_BUILTIN as an output.
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {

  if (now() > time_now) {

    // Reset the time_now to now
    time_now = now();
    
    // print out seconds:
    Serial.println(time_now);
    
    // Sets the LED on or off depending on time_now being pair or odd
    digitalWrite(LED_BUILTIN, time_now % 2);
  }

}

I'm amazed that <Time.h> fails in this crude Blink example without the use of delay(), without any interruption!


UPDATE: The use of the function millis() allows Blink without delay() with no library... More here: https://www.arduino.cc/en/Tutorial/BlinkWithoutDelay

Show the error.
My guess is that you are using an operating system that does not differentiate between upper and lower case, like Windows which can create a conflict issue between a system file called time.h and and the library file called Time.h (Those are NOT the same file)
i.e. Windows treats the filenames Time.h and time.h the same, even though they are not the same file so if there is a system file called time.h , which there is for some Arduino cores, then Windows will fail to build things correctly since it can grab time.h instead of Time.h which will cause problems since it is the wrong header file.

This is not really a problem with the library and it is not a problem for other operating systems that don't ignore the case of letters in filenames. It is a Windows problem in the way Windows handles filenames.

To work around this Windows issue, a file called TimeLib.h was created in the Time library.
So if you are using Windows, you will need to include TimeLib.h and not Time.h
Simple as that.
Also, using TimeLib.h will work for all the other operating systems that don't have this case issue.

I believe that there is some kind of registry option in Windows to disable case ignoring, but turning it on is likely to break other things that may unexpectedly depending on this behavior.

--- bill

bperrybap:
Show the error.

The error:

Arduino: 1.8.0 (Windows 7), Board: "Arduino Nano, ATmega328"

SimpleTimeLibrary:4: error: 'now' was not declared in this scope

 int time_now = now();

                    ^

C:\Users\Rui\Documents\Arduino\Rui\SimpleTimeLibrary\SimpleTimeLibrary.ino: In function 'void loop()':

SimpleTimeLibrary:16: error: 'now' was not declared in this scope

   if (now() > time_now) {

           ^

exit status 1
'now' was not declared in this scope

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

bperrybap:
i.e. Windows treats the filenames Time.h and time.h the same, even though they are not the same file so if there is a system file called time.h , which there is for some Arduino cores, then Windows will fail to build things correctly since it can grab time.h instead of Time.h which will cause problems since it is the wrong header file.

If true, considering the people that uses Windows, is quite an Arduino API Fail...

I will use the function millis() instead avoiding this way any library inclusion, like my own Blink Without Delay sketch:

// start previousMillis variable in milliseconds
unsigned long previousMillis = millis();
  
void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);

  // initialize digital pin LED_BUILTIN as an output.
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {

  if (millis()/1000 > previousMillis/1000) {

    // Reset the previousMillis to now
    previousMillis = millis();
    
    // print out current milliseconds:
    Serial.println(previousMillis);
    
    // Sets the LED on or off depending on previousMillis/1000 being pair or odd
    digitalWrite(LED_BUILTIN, previousMillis/1000 % 2);
  }

}

You didn't show the full output from the build, but you appear to be using Windows and this is the type error you will get when this issues occurs.

ruiseixas:
If true, considering the people that uses Windows, is quite an Arduino API Fail...

There is no if about it. Windows ignoring case in file names creates this issue.
Windows is a total fail for ignoring case on file names.
Redmond had the opportunity to fix it back in the mid 90's when NT was being developed and in fact the developers did fix this, along with many other issues like using backward slashes for directory paths and even removing the DOS volume letters that were used by CPM that they licensed from Seatle Computing to originally get their initial version of Microsoft "DOS".
The early pre-release versions of NT were great, it used gcc for the toolset, a unified filesystem with no bogus drive letters, no backward slashes for directory separators, and used dashes for command line options instead of slashes.
In the end, MSFT decided to preserve all the "DOS" compatibility in NT by default when it was released (even in the XP version) and so people are still stuck with all those limitations today.

In terms of being an Arduino API fail, it is not a API file. Regardless of which header file you include, the API does not change. So it is not an API issue at all. This is more of a documentation issue.

You have understand that the Time library has been out for quite some time, long before there was any Arduino core that contained its own version of a time library that had a file called time.h
So, for MANY years there was no conflict. It was only recently that there was an issue. And also keep in mind that this is only an issue for certain cores and certain operating systems. So not everyone will see the issue.
AND don't forget that as soon as this issue showed up on Windows a fix was put into the Time library by adding the header file TimeLib.h to fix it for those operating systems (Windows for the most part) that are using a core that contains a time.h header file.

And finally, had you looked at the examples included in the Time library, they now all use TimeLib.h not Time.h

The reason that Paul didn't remove Time.h from the library when TimeLib.h was added is it would have potentially broken lots of existing code that is still working. And so while the library and the examples were fixed to support and use TimeLib.h, the old Time.h header file was left in to try to preserve backward compatibility so as not to break existing code that is still working.

One thing I will say is that I would highly discourage using the Arduino include library function from the IDE.
It can add incorrect header files to your code.
I recommend reading the documentation and looking at the examples to see what headers need to be included and add the necessary includes yourself.

[quote
I will use the function millis() instead avoiding this way any library inclusion, like my own Blink Without Delay sketch:

[/quote]
For your simple application, that would be best, as it seems that all you were using it for was measuring delays and actually trying or needed to do epoch based time tracking.
There are other several libraries that are designed to solve what you are doing that would be much better to use than the Time library.

--- bill

bperrybap:
And finally, had you looked at the examples included in the Time library, they now all use TimeLib.h not Time.h

So, like arduino.cc is the same as ARDUINO.CC :slight_smile: and if you want a different URL address you give a REAL different URL address! Names that differ only in caps are ambiguous names!

ruiseixas:
So, like arduino.cc is the same as ARDUIO.CC :slight_smile: and if you want a different URL address you give a REAL different URL address! Names that differ only in caps are ambiguous names!

Upper and lower case letters are not ambiguous.
Uppercase and Lowercase letters are different codepoints.
Some but not all systems choose to ignore those differences.
DNS chose to ignore it, but DNS doesn't have anything to do with how a filesystem chooses to handle filename characters.

NT chose to ignore case in filenames by default just to be compatible with DOS which didn't have the capability to support all the codepoints and hence couldn't support upper and lower case.

Where things get all FUBAR is when systems like Windows do cutesy things in Explorer like changing and overriding filenames and directory names that they show the user to things like inserting/changing filenames to include the word "My" instead of using the actual filename/directory name which includes the user login name, or displaying the filename of the file using letters that are not actually in the filename. i.e. capitalizing the first letter and making the others lower case, regardless of the actual case of the letters used in the filename.

These kinds of things create problems, and the issue with Time.h vs time.h is just one example.

But to be more pendantic, the real issue here is in the IDE, the IDE is placing the search for system headers before the -I entries for the libraries. This is wrong. Normally when the compiler tools are installed properly, you don't have to use a -I options to tell the compiler where the system headers are.
And if you use -I options for other header directories, they are searched before the system headers.
The problem with the IDE is that it uses a set of compiler tools that are not installed properly, so it must use a -I option to tell the compiler where the system headers are located.
However, it places that -I up front ahead of all the other -I options for the Arduino libraries, this changes the normal/default behavior of how other -I options behave.
In the case of the Arduino IDE, what this means is that the compiler system headers will be searched first instead of last.
If the IDE were corrected to search the system headers last instead of first, this issue of time.h vs Time.h would go away on Windows since the compiler would find Time.h before it found time.h since it would search for a non case sensitive file in the Time library and find it before it would look in the system header area.

--- bill

Or Apple hiding extensions.

if you're using the same time library I am, which it seems like you are because I had the same questions when I first implemented it....then the time.h file in the time-master folder only has one line code in it and that is

#include "TimeLib.h"

if this is the case then I think TimeLib.h is the only header you should need to include

bperrybap:
Upper and lower case letters are not ambiguous.

Well, when I say ambiguous I'm saying in the optic of Human perception, is like when you call someone, you can't call someone or something and say, I was calling the one with capital T... I think that the new name TimeLib.h is the right way to go, now when someone says "timelib" no one needs to ask "the one with the capital T?"!

BTW, here is my ultra small Blink Without Delay sketch:

void setup() {
  // initialize digital pin LED_BUILTIN as an output
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  // sets the LED on or off depending on millis()/1000 being pair or odd
  digitalWrite(LED_BUILTIN, millis()/1000 % 2);
}

Can you do it smaller? :wink:

ruiseixas:
Can you do it smaller? :wink:

Sure :wink:

void setup(){pinMode(LED_BUILTIN,OUTPUT);}
void loop(){digitalWrite(LED_BUILTIN,millis()/1000%2);}

However, this code, as well as yours abuses/violates the digitalWrite() API.
The API for digitalWrite() as documented here: digitalWrite() - Arduino Reference
The documentation is very clear that digitalWrite(pin,value) takes two parameters:

  • pin
  • value

and that value is either HIGH or LOW. Using any other value is undefined.
So the behavior when the value is explicitly 0 or 1 is undefined.
While LOW and HIGH are typically 0 and 1 there is no requirement that they be those values therefore there should be no expectation of what those values are and how the underlying implementation uses them.
At least this code will never use a value larger than 1.
I see many cases where code assumes that underlying digitalWrite() code will treat any non zero value as HIGH which can potentially break depending on how the digitialWrite() code is implemented.
i.e does digitalWrite() look for LOW and and assume HIGH if the value is not LOW or does it look for HIGH and assume LOW if the value is not HIGH.

This is why abusing APIs is risky and it is best to always use the API the way it is documented.

An API conformant implementation would be:

void setup(){pinMode(LED_BUILTIN,OUTPUT);}
void loop(){digitalWrite(LED_BUILTIN,millis()/1000%2?HIGH:LOW);}

I have an Arduino MKR NB 1500 that needs the TimeLib routines. I found that when I compile on a Windows 10 PC it fails (see below) but on a Linux computer it compiles without error. This is related to Arduino topic: Confusion with Time.h and TimeLib.h! - Programming Questions - Arduino Forum
The errors flagged routines that were not even in TimeLib, such as, mktime() and strptime(). I think these are part of precompiled code that goes with the ARM processor. Notes in TimeLib.h talk about this. Here's the errors generated when trying to use NB.getTime():

\Arduino\libraries\MKRNB\src\NB.cpp: In member function 'long unsigned int NB::getTime()':
\Arduino\libraries\MKRNB\src\NB.cpp:444:13: error: aggregate 'NB::getTime()::tm now' has incomplete type and cannot be defined
struct tm now;
^~~
\Arduino\libraries\MKRNB\src\NB.cpp:451:7: error: 'strptime' was not declared in this scope
if (strptime(response.c_str(), "+CCLK: "%y/%m/%d,%H:%M:%S", &now) != NULL) {
^~~~~~~~
\Arduino\libraries\MKRNB\src\NB.cpp:451:7: note: suggested alternative: 'strxfrm'
if (strptime(response.c_str(), "+CCLK: "%y/%m/%d,%H:%M:%S", &now) != NULL) {
^~~~~~~~
strxfrm
\Arduino\libraries\MKRNB\src\NB.cpp:454:21: error: 'mktime' was not declared in this scope
time_t result = mktime(&now);
^~~~~~
\Arduino\libraries\MKRNB\src\NB.cpp:454:21: note: suggested alternative: 'getTime'
time_t result = mktime(&now);
^~~~~~
getTime
Multiple libraries were found for "MKRNB.h"
Used: \Arduino\libraries\MKRNB
Multiple libraries were found for "TimeLib.h"
Used: \Arduino\libraries\Time-master
exit status 1
Error compiling for board Arduino MKR NB 1500.

I found referenced to time.h in the Arduino installation file. Also, Time.h just uses an #include TimeLib.h without any Time.cpp file. It seems that we don't need to use #include Time.h along with #include TimeLib.h. I modified the Time-master library to remove references to Time.h (see below) in TimeLib.h. All my programs that use TimeLib appear to work ok, including the MKR1500. This mod might be a way aroung the Windows blindness to capitalization. Here's my approach (BTW, make a safe copy of Time.h adn TimeLib.h before you make any changes). And remember to restart the IDE after making changes to any library files for these to take effect.

In Arduino/libraries/Time-master subdirectory

  • Delete Time.h
  • In TimeLib.h:
    Delete lines at the top
    #ifndef _Time_h
    #ifdef __cplusplus
    #define _Time_h

and delete lines at the bottom of TimeLib.h
#endif // __cplusplus
#endif /* _Time_h */

In .ino files, only use #include TimeLib.h, remove and #include time.h
Someone out there probably understands compiler operation better and might know whether this is an appropriate fix and why #include Time.h is causing problems sometimes. Comments welcome.

Sounds like you just need to quite using Windows... :wink:

A bit related, but a different potential issue:
Anytime there is a libraries subdirectory like "libraryname-master" or "libraryname-##.##.##", that is a library that was not installed properly.
That is usually an indication that the library was installed using a zip file from github and it either the IDE used the github to level directory name in the zip file (which is incorrect) or the file was manually extracted into the libraries directory which again created an incorrect subdirectory name for the library.
This can cause issues with multiple versions of the library being installed if using versions from github tags.
I would recommend renaming the directory to be just the library name which is what is what it should be.

Related to your specific issue, what could be happening is that Windows doesn't support upper/lower case in filenames since it ignores case in filenames.

So to windows, Time.h and time.h are the same filename but to linux they are different filenames.

If you include <Time.h> in Arduino code when compiling on windows, you can end up getting <time.h> from the gcc tools that came with the IDE instead of the <TimeLib.h> from the Arduino Time library. This is is not what is wanted.
The reverse can also happen depending on IDE version and core.

If you want to use the Arduino Time library you should always include <TimeLib.h> rather than <Time.h> since that will work on Windows.

Older versions of the IDE didn't have this issue since older versions of the IDE used older gcc tools, which didn't come with unix time library functions and the associated time.h header file.

If you want to see what is really happening add some a #warning to your Time.h and TimeLIb.h header files and see if they are being included when you expect them to be. My guess is that there is a <Time.h> include somewhere and on Windows it is including <time.h> instead.

--- bill

1 Like