How to get network DateTime.Now()?

Viewed 7235

Actually in flutter DateTime.now() is returns device date and time. Users sometimes change their internal clock and using DateTime.now() can give wrong result.

  1. How can i get Network/Server Current DateTime in flutter ?
  2. Is it possible to get Network/Server Current DateTime Without using any packages ?

Thanks in advance!

5 Answers

It's not possible without any API. You can use ntp plugin:

A plugin that allows you to get precise time from Network Time Protocol (NTP). It implements the whole NTP protocol in dart.

This is useful for time-based events since DateTime.now() returns the time of the device. Users sometimes change their internal clock and using DateTime.now() can give the wrong result. You can just get clock offset [NTP.getNtpTime] and apply it manually to DateTime.now() object when needed (just add offset as milliseconds duration), or you can get already formatted [DateTime] object from [NTP.now].

Add this to your package's pubspec.yaml file:

dependencies:
  ntp: ^1.0.7

Then add the code like this:

import 'package:ntp/ntp.dart';

Future<void> main() async {
  DateTime _myTime;
  DateTime _ntpTime;

  /// Or you could get NTP current (It will call DateTime.now() and add NTP offset to it)
  _myTime = await NTP.now();

  /// Or get NTP offset (in milliseconds) and add it yourself
  final int offset = await NTP.getNtpOffset(localTime: DateTime.now());
  _ntpTime = _myTime.add(Duration(milliseconds: offset));

  print('My time: $_myTime');
  print('NTP time: $_ntpTime');
  print('Difference: ${_myTime.difference(_ntpTime).inMilliseconds}ms');
}

Try using the world clock api. Also, know that there is a chance the api could fail at some point... So I would recommend using a try-catch block around the http call, and if it does happen to fail, just return regular local time of the device....

  Future<void> getTime()async{
  var res = await http.get(Uri.parse('http://worldclockapi.com/api/json/est/now'));
  if (res.statusCode == 200){
  print(jsonDecode(res.body).toString());
}}

You can use this plugin ntp: ^2.0.0

import 'package:ntp/ntp.dart';

final int offset = await NTP.getNtpOffset(
        localTime: DateTime.now(), lookUpAddress: "time.google.com");
DateTime internetTime = DateTime.now().add(Duration(milliseconds: offset));

Or there are plenty of API's available

here is an example GET API for Indian time

http://worldtimeapi.org/api/timezone/Asia/Kolkata

The response will be like

  {
      "abbreviation": "IST",
      "client_ip": "45.125.117.46",
      "datetime": "2022-02-26T10:50:43.406519+05:30",
      "day_of_week": 6,
      "day_of_year": 57,
      "dst": false,
      "dst_from": null,
      "dst_offset": 0,
      "dst_until": null,
      "raw_offset": 19800,
      "timezone": "Asia/Kolkata",
      "unixtime": 1645852843,
      "utc_datetime": "2022-02-26T05:20:43.406519+00:00",
      "utc_offset": "+05:30",
      "week_number": 8
    }

If you dont know your country zone just call this API to get all timezones in the world

http://worldtimeapi.org/api/timezone/

This is my network time DateTime getNow() method. It is a global method that only gets the network time every two minutes. My app is not super time sensitive but I have run into some issues with people's clock's being off by a couple minutes. It just pings worldtimeapi.org at most every 2 minutes (you will get errors if you ping them too often) and uses their returned time to store an offset to modify the local datetime by. If there are errors with the http call, it falls back to the user's time. I'm also tracking the number of calls to this method just to help debug some timers I have to make sure they are getting disposed of properly.

The problem I had with worldclockapi is it was only accurate to the minute. I could have been doing something wrong but I solved it with using a different api. Here is the code:

int _nowOffset = 0;
int _lastHttpGet = 0;
int _nowCalls = 0;
Future<DateTime> getNow() async {
  try {
    _nowCalls++;
    DateTime nowLocal = DateTime.now();
    if ((nowLocal.millisecondsSinceEpoch - _lastHttpGet) > (oneMinuteMilliSeconds * 2)) {
      _lastHttpGet = nowLocal.millisecondsSinceEpoch;
      var res = await http.get(Uri.parse('https://worldtimeapi.org/api/timezone/Etc/UTC'));
      if (res.statusCode == 200) {
        //print(jsonDecode(res.body).toString());
        Map<String, dynamic> json = jsonDecode(res.body);
        DateTime nowHttp = DateTime.parse(json['datetime']);
        _nowOffset = nowLocal.millisecondsSinceEpoch - nowHttp.millisecondsSinceEpoch;
        if (_nowOffset > 0) {
          _nowOffset *= -1;
        }
        log('http $_nowCalls');
        return nowHttp;
      }
    }
    return DateTime.fromMillisecondsSinceEpoch(nowLocal.millisecondsSinceEpoch + _nowOffset);
  } catch (e, stack) {
    log('{http error: now calls: $_nowCalls $e\n $stack}');
    return DateTime.fromMillisecondsSinceEpoch(DateTime.now().millisecondsSinceEpoch + _nowOffset);
  }
}

I decided to go at this a different route since I'm already using Firebase. I had never used cloud functions before but I like this option better since I'm not reliant on api calls (some of which consider a ping more than once every four minutes a denial of service attack).

  1. firebase init functions
  2. flutter pub add cloud_functions
  3. In the generated index.js file in the functions folder add this code for the cloud function:
    const functions = require("firebase-functions");
    const admin = require('firebase-admin');
    admin.initializeApp();
    
    exports.timestamp = functions.https.onCall((data, context) => {
        // verify Firebase Auth ID token
        if (!context.auth) {
            return 'Authentication Required!';
        }
        let date = new Date();
        return date.toJSON();
    });
  1. firebase deploy --only functions Then in the dart code to call the function looks like this to return a network DateTime:
    final _functions = FirebaseFunctions.instance;
    
      Future<DateTime> getDateTime() async {
        try {
          final result = await _functions.httpsCallable('timestamp').call();
          return DateTime.parse(result.data);
        }on FirebaseFunctionsException  catch (error) {
          log(error.code);
          log(error.message!);
          log(error.details);
          throw Exception('Error getting datetime from cloud function.');
        }
      }
Related