I'm developing a small project, based on a ESP8266 module, to get date & time via an NTP server, since a RTC module wouldn't work for me. Iv'e tested a code where I get date & time using the NTPClient library in my circuit.
My system prints time just fine, it's accurate and synchronized with the timezone in my country. The problem comes regarding the date, because it gets inconsistent (and maybe random) data.
I mean, every time I load the code to my board, I get a different month, day and year, like if it was randomized so I can't just use an "offset" to set a date, because I don't know if the next time I load it it's gonna be the same number (it could print the current month is either january, september, may...).
Some examples from the Seril Monitor are these:
06:15:16.430 -> Epoch Time: 1640153824
06:15:16.430 -> Formatted Time: 06:17:04
06:15:16.430 -> Hour: 6
06:15:16.430 -> Minutes: 17
06:15:16.430 -> Seconds: 4
06:15:16.430 -> Week Day: Wednesday
06:15:16.430 -> Month day: 17
06:15:16.430 -> Month: 12
06:15:16.430 -> Month name: December
06:15:16.430 -> Year: 1339088
06:15:16.430 -> Current date: 1339088-12-17
BTW, I've tried changing the NTP server "link" to other than just "pool.ntp.org", to "us.pool.ntp.org", "fr.pool.ntp.org", among many others, but the result is the same...
So, I would like to know, what's wrong with my code regarding the date, and what may I do to improve it? Or if I could just change the NTP server...
This is my current code:
#include <ESP8266WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
// Replace with your network credentials
const char *ssid = "mySSID";
const char *password = "mypass";
// Define NTP Client to get time
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org");
//Week Days
String weekDays[7]={"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
//Month names
String months[12]={"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};
void setup() {
// Initialize Serial Monitor
Serial.begin(115200);
// Connect to Wi-Fi
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
// Initialize a NTPClient to get time
timeClient.begin();
// Set offset time in seconds to adjust for your timezone, for example:
// GMT +1 = 3600
// GMT +8 = 28800
// GMT -1 = -3600
// GMT 0 = 0
timeClient.setTimeOffset(-14400);
}
void loop() {
timeClient.update();
unsigned long epochTime = timeClient.getEpochTime();
Serial.print("Epoch Time: ");
Serial.println(epochTime);
String formattedTime = timeClient.getFormattedTime();
Serial.print("Formatted Time: ");
Serial.println(formattedTime);
int currentHour = timeClient.getHours();
Serial.print("Hour: ");
Serial.println(currentHour);
int currentMinute = timeClient.getMinutes();
Serial.print("Minutes: ");
Serial.println(currentMinute);
int currentSecond = timeClient.getSeconds();
Serial.print("Seconds: ");
Serial.println(currentSecond);
String weekDay = weekDays[timeClient.getDay()];
Serial.print("Week Day: ");
Serial.println(weekDay);
//Get a time structure
struct tm *ptm = gmtime ((time_t *)&epochTime);
int monthDay = ptm->tm_mday;
Serial.print("Month day: ");
Serial.println(monthDay);
int currentMonth = ptm->tm_mon+1;
Serial.print("Month: ");
Serial.println(currentMonth);
String currentMonthName = months[currentMonth-1];
Serial.print("Month name: ");
Serial.println(currentMonthName);
int currentYear = ptm->tm_year+1900;
Serial.print("Year: ");
Serial.println(currentYear);
//Print complete date:
String currentDate = (String) currentYear + "-" + (String) currentMonth + "-" + (String) monthDay;
Serial.print("Current date: ");
Serial.println(currentDate);
Serial.println();
delay(1000);
}
This is a bug in recent versions of the NTPClient library on the esp8266.
Downgrading to 2.7.4 of NTPClient appears to resolve the issue for some users.
See https://github.com/arduino-libraries/NTPClient/issues/149
Related
Somebody asked this question as a comment in an answer I already posted to another question. Its awkward and confusing to have extra questions in comments, so I'm posting this as a new question.
The new question is this:
How can i check if my timestamp is Less than 2:00 am the next day?
The Foundation framework has a rich set of functions to do "Calendrical Calculations".
The Calendar class has functions for adding "date component" values to dates, or forcing an existing Date to a specific time-of-day. We can combine those to give a variety of results.
Edit:
Leo Dabus pointed out in the comments that there is a built-in Calendar function nextDate(after:matching:options:) that lets us calculate a specific time in the next day in one shot.
That version of the function looks like this:
extension Date {
func twoAmNextDay() -> Date {
let components = DateComponents(hour: 2, minute: 0, second: 0)
return Calendar.current.nextDate(after: self, matching: components, matchingPolicy: .nextTime)!
}
Note that my implementation uses a force-unwrap to make the result non-optional. That's bad form. It would be better to make the function throw or return an Optional.
For completeness, my original function was this:
extension Date {
func twoAmNextDay() -> Date {
guard let tomorrow = Calendar.current.date(byAdding: .day, value: 1, to: self),
let result = Calendar.current.date(bySettingHour: 2, minute: 0, second: 0,of: tomorrow) else { fatalError() }
return result
}
}
I don't know if the nextDate(after:matching:options:) based version handles things like the transition between daylight savings time and standard time better, but it's certainly simpler.
We can include that extension with some other code to make a working demo:
extension Date {
func twoAmNextDay() -> Date {
let components = DateComponents(hour: 2, minute: 0, second: 0)
return Calendar.current.nextDate(after: self, matching: components, matchingPolicy: .nextTime)!
}
func localDateString(dateStyle: DateFormatter.Style = .medium, timeStyle: DateFormatter.Style = .medium) -> String {
DateFormatter.localizedString(from: self, dateStyle: dateStyle, timeStyle: timeStyle)
}
func isOtherDateBefore2AmNextDay(_ otherDate: Date) -> Bool {
otherDate < self.twoAmNextDay()
}
}
let now = Date()
let secondsInDay = 24.0 * 60.0 * 60.0
print( "Current date = " + now.localDateString() + ". Tomorrow at 2:00 AM is " + Date().twoAmNextDay().localDateString() + "\n")
for _ in 1...5 {
let randomOffset = Double.random(in: -secondsInDay*3...secondsInDay*3)
let randomizedDate = now.addingTimeInterval(randomOffset)
print("Randomized date = " + randomizedDate.localDateString())
let notString = now.isOtherDateBefore2AmNextDay(randomizedDate) ? "" : "not "
print("Randomized date is " + notString + "before 2AM tomorrow")
print()
}
Sample output from that code looks like this:
Current date = Sep 2, 2022 at 11:07:15 AM. Tomorrow at 2:00 AM is Sep 3, 2022 at 2:00:00 AM
Randomized date = Sep 1, 2022 at 1:59:02 AM
Randomized date is before 2AM tomorrow
Randomized date = Sep 3, 2022 at 6:21:36 AM
Randomized date is not before 2AM tomorrow
Randomized date = Sep 3, 2022 at 9:13:19 PM
Randomized date is not before 2AM tomorrow
Randomized date = Sep 3, 2022 at 9:17:17 PM
Randomized date is not before 2AM tomorrow
Randomized date = Aug 30, 2022 at 11:49:46 PM
Randomized date is before 2AM tomorrow
I am trying to get a list of months between two dates in Dart
Example :
Input Date:
date 1 - 20/12/2011
date 2 - 22/08/2012
Now, my expected result should be :
12/2011
1/2012
2/2012
3/2012
4/2012
5/2012
6/2012
7/2012
8/2012
Thanks for your help!
You can do this:
var date1 = DateFormat("dd/MM/yyyy").parse("20/12/2021");
var date2 = DateFormat("dd/MM/yyyy").parse("22/08/2022");
while (date1.isBefore(date2)) {
print(DateFormat("M/yyyy").format(date1));
date1 = DateTime(date1.year, date1.month + 1);
}
You need
import 'package:intl/intl.dart';
Not exactly what OP has asked for, but I wanted to share this solution which takes me a while.
This solution also considers the day, and handle correctly the case for February 28.
static List<DateTime> extractMonthsInRange(DateTime from, DateTime to) {
//0. save day of from date
var daysInFromDate = from.day;
List<DateTime> monthsInRange = [DateTime(from.year, from.month)];
//1. get a list of months between 2 dates (without days)
while (from.isBefore(DateTime(to.year, to.month - 1))) {
var newFrom = DateTime(from.year, from.month + 1);
monthsInRange.add(newFrom);
from = newFrom;
}
//2. iterate months
return monthsInRange.map((month) {
var _lastDayOfMonth = lastDayOfMonth(month);
//2. if the day of the from date is < then the day of the last day of the month using the daysInFromDate
if (daysInFromDate < _lastDayOfMonth.day) {
return DateTime(month.year, month.month, daysInFromDate);
}
//3. else save the last day of the month (means that the month has less days)
return _lastDayOfMonth;
}).toList();
}
/// The last day of a given month
static DateTime lastDayOfMonth(DateTime month) {
var beginningNextMonth = (month.month < 12)
? DateTime(month.year, month.month + 1, 1)
: DateTime(month.year + 1, 1, 1);
return beginningNextMonth.subtract(Duration(days: 1));
}
Please feel free to improve my code in the comments.
you can use
date1.subtract(Duration(days: 7, hours: 3, minutes: 43, seconds: 56));
date1.add(Duration(days: 1, hours: 23)));
to add oder subtract days, months ...or what u like.... then do a if check in a loop or a while loop
something like this:
void main() {
DateTime a = DateTime.now();
DateTime b = a.add(Duration(days: 360));
DateTime c = a;
while (c.millisecondsSinceEpoch<b.millisecondsSinceEpoch)
{
c = c.add(Duration(days:31));
print('${c.month}/${c.year}');
}
}
So, I'm working on a converter for changing JDE Julian Dates to Gregorian Dates and Vice Versa. I've run into a weird problem that I cannot find an answer for in my Julian to Gregorian code where non-leap years are calculating as leap years and leap years are calculating as non-leap years.
Exp: When I enter the JDE Julian Date '122095' it should return 04/05/2022 but instead returns 04/06/2022. When I changed the year to 2020, a leap year, it returned todays correct date of 04/05/2022 when it should have returned 04/04/2020.
I think the problem might be with my DateTime.fromMillisecondsSinceEpoch converter because it is return 2022-04-06 06:00:00.000Z and I don't know where the 6 in the hour spot is coming from or how to cancel it out. My Code is below.
Edit: I forgot, I used the code originally from THIS post.
Edit Edit: Okay that fixed the problem, I set "var millisecondsSinceEpoch = DateTime(convYear()).millisecondsSinceEpoch;" to "var millisecondsSinceEpoch = DateTime.utc(convYear()).millisecondsSinceEpoch;" and it was still giving me the wrong answer. I then thought to take the "isUtc: True" off of the end of "var dayOfYearDate = DateTime.fromMillisecondsSinceEpoch((millisecondsSinceEpoch + millisDayOfYear));" and that fixed the issue. The best I can figure is having the UTC bool at the end of that line was preventing millisecondsSinceEpoch from actually converting to UTC.
Thanks jamesdlin and James Heap for your help!
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
gregDate(var inputDate) {
var inputDate = '122095';
// Break down JDE date into individual pieces
var currentYear = int.parse(DateFormat('yy').format(DateTime.now()));
print('Current Year: $currentYear');
var splitYear = int.parse(inputDate.substring(1, 3));
print('Split Year: $splitYear');
var splitDay = int.parse(inputDate.substring(3, inputDate.length));
print('Split Day: $splitDay');
// Uses the current year to determine if our 2 digit date needs a 19 or 20 in the front
convYear() {
if (splitYear <= currentYear) {
var year = '20' + splitYear.toString();
return int.parse(year);
}
else {
var year = '19' + splitYear.toString();
return int.parse(year);
}
}
print('Converted Year: ${convYear()}');
// Takes 3 digit day in year and converts it to formatted date time.
convDate() {
var dayOfYear = splitDay;
var millisInADay = Duration(days: 1).inMilliseconds; // 86400000
print(millisInADay);
var millisDayOfYear = dayOfYear * millisInADay;
print(millisDayOfYear);
var millisecondsSinceEpoch = DateTime.utc(convYear()).millisecondsSinceEpoch;
print(millisecondsSinceEpoch);
var dayOfYearDate = DateTime.fromMillisecondsSinceEpoch((millisecondsSinceEpoch + millisDayOfYear), isUtc: true);
var result = DateFormat('MM/dd/${convYear()}').format(dayOfYearDate);
print(dayOfYearDate);
print(result);
return result;
}
return convDate();
}
I am making use of an API which states the following:
NOTE: All times are in UTC and summer time adjustments are not included in the returned data.
So I retrieved the data as follows:
String time = "3:52:59 PM";
I want to get that time as the phone localtime, so according to a similar question for dates it would be as follows:
final convertedTime = DateTime.parse(time).toLocal();
print(convertedTime.toString());
But this produces an error:
Invalid date format
I guess this issue comes because date time expects a date and not just hours, but I am not able to find anything else for just hours in import 'package:intl/intl.dart';
How can I make this conversion, for example for GMT+1 taking into account the summertime, or any other timezone of the phone?
In my example the expected time would be:
String time = "4:52:59 PM";
If you know the received time is in UTC and the date are "today" in UTC time, we can do something like this:
void main() {
print(parseHoursInUtc('3:52:59 PM')); // 2021-01-21 15:52:59.000Z
}
RegExp _timePattern =
RegExp(r'(?<hour>\d+):(?<minute>\d+):(?<second>\d+) (?<amPm>AM|PM)');
// timeString must be of the format "3:52:59 PM"
DateTime parseHoursInUtc(String timeString) {
final match = _timePattern.firstMatch(timeString);
final hour = int.parse(match.namedGroup('hour'));
final minute = int.parse(match.namedGroup('minute'));
final second = int.parse(match.namedGroup('second'));
final isPm = match.namedGroup('amPm') == 'PM';
final now = DateTime.now().toUtc();
return DateTime.utc(
now.year, now.month, now.day, isPm ? hour + 12 : hour, minute, second);
}
The methods returns a DateTime in UTC mode which you can convert to local time with toLocal().
You can then use the intl package to create a DateTimeFormat which prints the time as you want it like this:
import 'package:intl/intl.dart';
void main() {
print(DateFormat.jms().format(parseHoursInUtc('3:52:59 PM').toLocal())); // 4:52:59 PM
}
RegExp _timePattern =
RegExp(r'(?<hour>\d+):(?<minute>\d+):(?<second>\d+) (?<amPm>AM|PM)');
// timeString must be of the format "3:52:59 PM"
DateTime parseHoursInUtc(String timeString) {
final match = _timePattern.firstMatch(timeString);
final hour = int.parse(match.namedGroup('hour'));
final minute = int.parse(match.namedGroup('minute'));
final second = int.parse(match.namedGroup('second'));
final isPm = match.namedGroup('amPm') == 'PM';
final now = DateTime.now().toUtc();
return DateTime.utc(
now.year, now.month, now.day, isPm ? hour + 12 : hour, minute, second);
}
I am in Denmark which are CET (UTC+1) right now (and with now I mean outside DST).
To get the time of any date in flutter you can use:
DateFormat("hh:mm aaa").format(DateTime.now()))
result: 5:40 p.m
My situation is as follows: I get a ticket info from an API Call, and I can see in my Realm Browser, that the dates of when I activated the ticket and when it expires are saved correctly in the database in UTC.
In the Database, using Realm Browser, I can see that startTime is Apr 25, 2017, 1:45:30 PM and endTime is Apr 26, 2017, 6:45:30 AM. (My local time was 9:45:30 AM at the time of activating my ticket - so this is correctly setup on servers end)
However, when I access that date later on in code and retrieve it from database it gives me a date with an offset!!! (And no, it's not a date in local timezone - it should've been a date saved in UTC).
Here's some code I use to get the info from database and display it:
func getTickets() -> [Ticket] {
let tickets = userInfo?.tickets.filter("state == %#", "activated").map({ (dbTicket) -> Ticket in
var startTime: Date? = nil
var endTime: Date? = nil
if let start = dbTicket.startTime, let end = dbTicket.endTime {
print("START ", dbTicket.startTime,
"\nNOW ", NSDate(),
"\nEND ", dbTicket.endTime)
startTime = start as Date
endTime = end as Date
}
print("START ", dbTicket.startTime,
"\nNOW ", Date(),
"\nEND ", dbTicket.endTime)
return Ticket(id: dbTicket.id, startTime: startTime, endTime: endTime)
}) ?? []
return tickets
}
And here's what gets printed in the console:
START Optional(2017-04-25 17:45:30 +0000)
NOW 2017-04-25 13:46:15 +0000
END Optional(2017-04-26 10:45:30 +0000)
START Optional(2017-04-25 17:45:30 +0000)
NOW 2017-04-25 13:46:15 +0000
END Optional(2017-04-26 10:45:30 +0000)
Which is incorrect! START should be almost the same as NOW. So why START and END dates are read from Realm database incorrectly ? Especially that I can see then in the Realm Browser and there they are saved correctly.
NSDate represents a specific point in time, independent of any time zone. The only time that a time zone is associated with an NSDate is when creating a string representation of it (in your case, this happens when print ends up calling -[NSDate description]). If you want to control which time zone the date is formatted with you can explicitly convert NSDate to the string using NSDateFormatter, which allow you to control the time zone that's used.