How do I create a DateTime instance in a specific timezone? - dart

I am calling a legacy service that returns all times in a string format assuming that the timezone is EST. How do I create a DateTime instance and specify that the timezone is EST? When I display the times to the user's I will translate to their device timezone.

I wrote a package for this. It's called Instant, and it can fetch the latest DateTime in any given zone worldwide. Take a detailed look at https://aditya-kishore.gitbook.io/instant/
The basic usage to get a DateTime in a timezone is fairly simple.
Note: I assume below that your legacy service delivers a String with format HH:MM in 24 hr time, and that it doesn't deliver a date. If any of the following is untrue, lemme know and I'll change the code sample.
List<int> _hourmin (String parse) {
List<int> timeinint = [];
if (parse.length == 5) {
//if parse is something like 10:00
timeinint.add(int.parse(parse.substring(0,2))); //Add hours (10)
timeinint.add(int.parse(parse.substring(3,5))); //Add minutes (0)
} //hours >= 10
if (parse.length == 4) {
//if parse is something like 4:06
timeinint.add(int.parse(parse.substring(0,1))); //Add hours (4)
timeinint.add(int.parse(parse.substring(2,4))); //Add minutes (6)
} //hours < 10
return timeinint;
}
DateTime utcDateTimeFromZone(
{#required String zone,
int year = 0,
int month = 0,
int day = 0,
int hour = 0,
int minute = 0,
int second = 0,
int millisecond = 0,
int microsecond = 0}) {
hour = (hour - timeZoneOffsets[zone].truncate()) % 24; //Convert to UTC from timezone
if (hour<0) {
hour = 24 + hour;
}
minute = (minute - ((timeZoneOffsets[zone]%1)*60).round()) % 60;
if (minute<0) {
minute = 60 + minute;
}
//Convert to UTC from timezone
DateTime inUTC = DateTime.utc(year, month, day, hour, minute, second, millisecond, microsecond); //Initialize in UTC time
return inUTC;
}
//We get an array containing the hours minutes
List<int> time = _hourmin("4:25"); //put your API call as the param here
//This is what you need
DateTime finished = utcDateTimeFromZone(zone: "EST", hour: time[0], minute: time[1]);
print(formatTime(time: finished.toLocal())); //1:25 for me, because I'm PST, but this works with any local timezone
Hope that clears it up!

The included DateTime class only supports the local timezone or UTC but no specific timezone.
https://pub.dartlang.org/packages/timezone would provide that but it looks like it needs an update for Dart 2.0.0
https://pub.dartlang.org/packages/time_machine is a newer one.

Related

Number of days in month returns wrong value after 10:00 PM

I am having a small issue with getting the total days in a month using Swift.
I have extended the Date class and created this function:
func daysInMonth() -> Int {
print(self.day) ##30
print(self.month) ##12
print(self) ## 2021-11-30 23:46:29 +0000
print(Calendar.current.range(of: .day, in: .month, for: self)?.count) ##31
return Calendar.current.range(of: .day, in: .month, for: self)?.count ?? 0
}
I have set the Date&Time to the 30th of November, at 11:45 PM in the settings of my Mac, in Preferences.
I called the above function at 11:46 PM and obtained the above results (inline, next to the print statements).
The date output is correct as well as the day. The month output is wrong and the result is 31 days in the month of November.
If I run this exact same code before 10:00 PM, I get the right result which is 30 days.
Does anyone know why this is happening?
Thank you,
Paprika
It's a GMT offset issue combined with the current day in a month.
When you create a date without set a day, it will be set to the first day of the month.
So, if your timezone offset is for example -4 means your are 4 hours behind the GMT 0 and by default the timezone defined at Calendar.current is equal the system timezone. So what it means? Means you'll obtain the previous month if you test it in a boundary of 23 + (-4) or the next month if your offset is positive.
You can test this behaviour copying'n paste the following code in the Playground.
func getDaysInMonth(month: Int, year: Int, offset: Int = 0) -> Int? {
let someDate = DateComponents(year: year, month: month, hour: 3)
var current = Calendar.current
let timezone = TimeZone(secondsFromGMT: 60 * 60 * offset)!
current.timeZone = timezone
guard let someDay = current.date(from: someDate) else { return nil }
print("date: \(someDay)") // this will always
return someDay.daysInCurrentMonth
}
for hour in -12...12 {
print("hour: \(hour)\ndays: \(getDaysInMonth(month: 10, year: 2021, offset: hour) ?? -1)")
print("---\n")
}
extension Date {
var daysInCurrentMonth: Int? {
Calendar.current.range(of: .day, in: .month, for: self)?.count
}
}
Notice the days will change starting by your current system time zone (notice only the month will change).
How to fix this?
In your case, I guess you just want to show how many days a month have, so you can just set the to zero like this:
TimeZone(secondsFromGMT: 0)
Do this change at a instance of Calendar.current and check if it works for you.
It appears there something wrong with your Date extension methods for .day and .month.
Without seeing code it's hard to determine what the problem is though. Below is some code for returning the current month (Int) and current numbered day of month (Int)
extension Date
{
var month: Int
{
let date = Date()
let calendar = Calendar.current
let components = calendar.dateComponents([.month], from: date)
return components.month
}
var day: Int
{
let date = Date()
let calendar = Calendar.current
let components = calendar.dateComponents([.day], from: self)
return components.day
}
}
Please also ensure your time/date settings are correct on your mac/simulator/device. If these are wrong - it could have been jumping to a different month if you were in a timezone that was ahead a few hours.

Trouble Initializing TimeZone from TimeZone.abbreviation()

I am storing user birth dates on my backend via storing a date component dictionary. It looks something like this:
{
"day": 1,
"month": 1,
"year": 1970,
"timeZone": "GMT"
}
To store this object, it grabs the user's birth day, month, and year from user input. The user time zone, however, is gathered via TimeZone.current.abbreviation().
Now, some of my user birthdate objects on my backend have their "timeZone" formatted as "CST", "BST", or "PDT". "timeZone"s that are formatted this way successfully initialize a TimeZone on the front end via let timeZone = TimeZone(abbreviation: "CST")!, let timeZone = TimeZone(abbreviation: "BST")!, or let timeZone = TimeZone(abbreviation: "PDT")!, respectively.
The problem is, other user birthdate objects on my backend have their "timeZone" formatted as "GMT+8". When trying to initialize "timeZone"s formatted like this via let timeZone = TimeZone(abbreviation: "GMT+8")!, the initialization returns nil. I also tried let timeZone = TimeZone(identifier: "GMT+8")!, but this returns nil as well.
Is there a way to initialize a TimeZone when it is formatted with respect to its offset to GMT as opposed to its unique abbreviation? I've seen a TimeZone initializer that is TimeZone(secondsFromGMT: Int). Could I simply take the 8 from "GMT+8" and multiply it by 3600 (the number of seconds in an hour) and pass this result to TimeZone(secondsFromGMT: Int)?
I ended up writing code that adapts my application to account for these unexpected fringe cases where a TimeZone's abbreviation is formatted like "GMT+8" rather than "SGT". I created an extension to TimeZone:
extension TimeZone {
static func timeZone(from string: String) -> TimeZone {
//The string format passed into this function should always be similar to "GMT+8" or "GMT-3:30"
if string.contains("±") {
//This case should always be "GMT±00:00", or simply GMT
return TimeZone(secondsFromGMT: 0)!
} else {
//If the string doesn't contain "±", then there should be some offset. We will split the string into timeZone components. "GMT+8" would split into ["GMT", "8"]. "GMT-3:30" would split int ["GMT","3","30"]
let timeZoneComponents = string.components(separatedBy: CharacterSet(charactersIn: "+-:"))
var isAheadOfGMT: Bool!
//Check if the string contains "+". This will dictate if we add or subtract seconds from GMT
if string.contains("+") {
isAheadOfGMT = true
} else {
isAheadOfGMT = false
}
//Grab the second element in timeZoneElements. This represents the offset in hours
let offsetInHours = Int(timeZoneComponents[1])!
//Convert these hours into seconds
var offsetInSeconds: Int!
if isAheadOfGMT {
offsetInSeconds = offsetInHours * 3600
} else {
offsetInSeconds = offsetInHours * -3600
}
//Check if there is a colon in the passed string. If it does, then there are additional minutes we need to account for
if string.contains(":") {
let additionalMinutes = Int(timeZoneComponents[2])!
let additionalSeconds = additionalMinutes * 60
offsetInSeconds += additionalSeconds
}
//Create a TimeZone from this calculated offset in seconds
let timeZoneFromOffset = TimeZone(secondsFromGMT: offsetInSeconds)!
//Return this value
return timeZoneFromOffset
}
}
}
It is used like so:
let json: [String:String] = ["timeZone":"GMT+8"]
let timeZone = json["timeZone"]
let birthDate: BirthDate!
if let timeZoneFromAbbrev = TimeZone(abbreviation: timeZone) {
birthDate = BirthDate(day: birthDay, month: birthMonth, year: birthYear, timeZone: timeZoneFromAbbrev)
} else {
let timeZoneFromOffset = TimeZone.timeZone(from: timeZone)
print(timeZoneFromOffset.abbreviation())
//Prints "GMT+8"
birthDate = BirthDate(day: birthDay, month: birthMonth, year: birthYear, timeZone: timeZoneFromOffset)
}
My BirthDate class for context:
class BirthDate {
var day: Int
var month: Int
var year: Int
var timeZone: TimeZone
init(day: Int, month: Int, year: Int, timeZone: TimeZone) {
self.day = day
self.month = month
self.year = year
self.timeZone = timeZone
}
}
Time zones are funny things to work with. If anybody sees issue with the TimeZone extension above, please let me know. I think I've accounted for all scenarios, but could be mistaken.

Converting hours to HH:MM format

I need to insert the duration of a task into Pipedrive in the format HH:MM.
I have made a small Java script using code in zapier to find the difference between start og end time and thereby find number of hours or minutes that is the duration.
But I don't know how to convert this into HH:MM using JavaScript. Any suggestions?
Thanks
Steve
What is the unit of measurement for the duration that you have?
If it's in minutes, you can use the below code as reference to convert minutes into HH:MM format.
The duration_minutes variable refers to a variable that holds the initial duration that you calculated.
//Calculate hours and minutes from the variable holding the duration
var hours = Math.floor(duration_minutes/60)
var minutes = duration_minutes%60
//Hours less than 10 need a prefix of 0
if (hours < 10){
hours = 0 + hours.toString();
}
else{
hours = hours.toString();
}
//Minutes less than 10 need a prefix of 0
if (minutes < 10){
minutes = 0 + minutes.toString();
}
else{
minutes = minutes.toString();
}
//Join hours and minutes together to make HH:MM format
var fullduration = hours + ":" + minutes;
If duration_minutes is 150, fullduration will become 02:30.

Formatting a date with Moment Tz

I'm struggling with formatting a date to insert into a calendar. I'm working on an Ionic project and I'm attempting to leverage moment.js timezones. The data is being passed into the application in several pieces. I am receiving a millisecond date time stamp, a 24-hour string for time and a string of the timezone. The date does not contain the time. What I want to achieve is creating a date object from these pieces and then converting the date into the user's local timezone to add it to their device calendar.
example of data passed to app
The date of the event: August 14, 2018 17:00
time = 17:00
date = 1534204800
timezone = AEDT
The destination timezone is based on the user's location.
let timeFormatter = new Date();
timeFormatter.setMilliseconds(date);
let momentHrMin = moment(timeFormatter.toDateString() + " " + time);
//WP sever is on GMT get the day, month and yr
let momentTZDate = momentTz.unix(date);
momentTZDate.tz('GMT');
let day = momentTZDate.days();
let month = momentTZDate.month();
let yr = momentTZDate.year();
//set the correct timezone on the ecpoh/unix DATE with momentTz.
momentTZDate.tz(this.eventDetails.eventDetail.timezoneOffset);
// Lastly set the date so the timezone conversions are correct
momentTZDate.set(
{
day: day,
month: month,
year: yr,
hour: momentHrMin.hour(),
minute: momentHrMin.minute(),
second: 0,
millisecond: 0
}
);
I found the cause of my struggles with the timezones. The abbreviated AEDT is not an accepted value for Moment.tz. AEDT could represent multiple timezones. For example, American Eastern, Australian Eastern, etc... Simply changing momentTZDate.tz("AEDT"); to momentTZDate.tz("Australia/Brisbane"); fixed my issue. Below is my typescript code I used to get through this issue. I won't say its the best but it works.
private getCalDate(date:number, time: string): Date {
// create an object in which the hours and minutes can be parse from(do to free text entry on the backend moment handles different inputs)
let timeFormatter = new Date();
timeFormatter.setMilliseconds(date);
let momentHrMin = moment(timeFormatter.toDateString() + " " + time);
//WP sever is on GMT get the day, month and yr
let momentTZDate = momentTz.unix(date);
momentTZDate.tz('GMT');
let day = momentTZDate.days();
let month = momentTZDate.month();
let yr = momentTZDate.year();
//set the correct timezone on the ecpoh/unix DATE with momentTz.
momentTZDate.tz("Australia/Brisbane");
// Lastly set the date so the timezone conversions are correct
momentTZDate.set(
{
day: day,
month: month,
year: yr,
hour: momentHrMin.hour(),
minute: momentHrMin.minute(),
second: 0,
millisecond: 0
}
);
return momentTZDate.toDate();
}

hh:mm aa (12 hour format ) convert in HH:mm (24 hour format)

I have String time="02:30 PM" means 12 hour format and i want to convert this time in 24 hour format .
I want this o/p: 14:30. so how can i convert this ?
I don't know anything about berries, but in case the API is missing a proper formatting function, you can always get your hands dirty with the string itself:
static String convert(String time){
boolean pm = "PM".equals(time.substring(6).toUpperCase());
int h = Integer.valueOf(time.substring(0,2));
if (h!=12)
h+=pm?12:0;
else
h=pm?h:0;
return ((h<10)?"0":"")+h + ":" + time.substring(3,5);
}
Try this code.
This will give the current time in 24 Hours format.
SimpleDateFormat formate = new SimpleDateFormat("HH:mm");
String newTime = formate.formatLocal(System.currentTimeMillis());

Resources