Noda time representation for close/open that is an entire day (24 hour period) - nodatime

I am parsing some interestingly formatted data from https://raw.githubusercontent.com/QuantConnect/Lean/master/Data/market-hours/market-hours-database.json
It contains a snippet (removing some days) as below:
"Future-cme-[*]": {
"dataTimeZone": "UTC",
"exchangeTimeZone": "America/Chicago",
"monday": [
{
"start": "00:00:00",
"end": "1.00:00:00",
"state": "market"
}
],
"friday": [
{
"start": "00:00:00",
"end": "16:00:00",
"state": "market"
}
]
}
I am using a JsonConverter<LocalTime> to convert the above, and I can parse the friday properties start and end without any issues into a LocalTime.
However, the dates which this represents as an entire day, i.e. 1.00:00:00 it throws an error on as it is not an ISO format - this opens up questions on my (incorrect!) use of the structures.
Currently I have the format that uses the LocalTime as below:
public class MarketHoursSegment
{
public LocalTime Start { get; init; }
public LocalTime End { get; init; }
public MarketHoursState State { get; init; }
}
And the formatter:
public class LocalTimeConverter : JsonConverter<LocalTime>
{
public override LocalTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return LocalTimePattern
.GeneralIso
.Parse(reader.GetString())
.GetValueOrThrow();
}
public override void Write(Utf8JsonWriter writer, LocalTime value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString());
}
}
Is there a preferred way to deal with a LocalTime that represents a 24 hours span?
Would I detect 1:00:00:00 in the reader.GetString()
of the converter, and if so set to 00:00:00 (midnight) and check if
Start == End then we know it is an entire 24 hour period?
Or would it be more correct to have the Start as a LocalTime,
and a Duration for the hours (i.e. 24 hours) with End => Start + Duration?

Is there a preferred way to deal with a LocalTime that represents a 24 hours span?
It's worth taking a step back and separating different concepts very carefully and being precise. A LocalTime doesn't represent a 24 hour span - it's just a time of day. Two LocalTime values could effectively represent a 24 hour span without reference to a specific date, yes.
If you can possibly change your JSON to use 00:00:00, and then treat a "start==end" situation as being the full day, that's what I'd do. That does mean, however, that you can never represent an empty period.
Now, in terms of whether you should use a start and duration... that really depends on what you're trying to model. Are you trying to model a start time and an end time, or a start time and a duration? So far you've referred to the whole day as "a 24 hour span" but that's not always the case, if you're dealing with time zones that have UTC offset transitions (e.g. due to daylight saving time).
Transitions already cause potential issues with local intervals like this - if you're working on a date where the local time "falls back" from 2am to 1am, and you've got a local time period of (say) 00:30 to 01:30, then logically that will be "true" for an hour and a half of the day:
00:00-00:30: False
00:30-01:30 (first time): True
01:30-02:00 (first time): False
01:00-01:30 (second time): True
01:30-02:00 (second time): False
02:00-00:00 (next day): False
We don't really know what you're doing with the periods, but that's the sort of thing you need to be considering... likewise if you represent something as "00:00 for 24 hours" how does that work on a day which is only 23 hours long, or one that is 25 hours long? It will very much depend on exactly what you do with the data.
I would adopt a process of:
Work out detailed requirements, including what you want to happen on days with UTC offset transitions in the specific time zone (and think up tests at this stage)
Extract the logical values from those requirements in terms of Noda Time types (with the limitation that no, we unfortunately don't support 24:00:00 as a LocalTime)
Represent those types in your JSON as closely as possible
Make your code follow your requirements documentation as closely as possible, in terms of how it handles the data

Related

Importing date without local time transformation

I am getting a date in DD-MMM-YY format, and am using dayjs to convert it to standard date format in nodejs.
However, the returned date is 1 day earlier than the input date.
I was told this is probably due to some difference in the server local time.
I can easily just add a day to the date, but as this will be a global function that works in multiple time zones, I just want to get the date "as is" without any automatic adjustments.
This is my function:
const convertDate = (date,format,zone) => {
dayjs.tz.setDefault(zone);
console.log(date);
console.log(dayjs(date));
console.log(dayjs.utc(date).$d);
console.log(dayjs.tz(date,format,zone).$d);
var newDate = dayjs.tz(date,zone);
//newDate.setDate(newDate.getDate());
return newDate;
}
No matter which methods I use or which zone I set, the date comes out as one day earlier than the input date.
For example, for a date of 01-APR-03 I get:
2003-03-31T21:00:00.000Z
I want the time to just be 2003-04-01T00:00:00.000Z.
Following comments, I have tried the following approach, but the result is the same:
const fixMonthName = (s) => s.replace(/[A-Z]{2}-/, (m) => m.toLowerCase());
const d = dayjs.utc(fixMonthName("22-FEB-02"), "DD-MMM-YY");
console.log(d);
const s = d.toISOString();
console.log(s); //{result:
M {
'$L': 'en',
'$u': true,
'$d': 2002-02-21T22:00:00.000Z,
'$x': {},
'$y': 2002,
'$M': 1,
'$D': 21,
'$W': 4,
'$H': 22,
'$m': 0,
'$s': 0,
'$ms': 0
}}
2002-02-21T22:00:00.000Z
Let's recap the problem:
You have a date-only string value of 01-APR-03 (equivalent to 2003-04-01).
You're then parsing it as timestamp, treating it as if it were 2003-04-01T00:00:00.000 (local time). This is the cause of the logical error.
Then you're looking at a UTC representation (2003-03-31T21:00:00.000Z in your example), and wondering why it's been shifted. (Z means UTC)
Fundamentally, a date and a timestamp are two different concepts. If you conflate them, you will have complications in your code such as the one you described.
A date can be thought of as a half-open range of timestamps (from the start of one day, to just before the start of the next). In other words, logically the following is true:
'2003-04-01' == ['2003-04-01T00:00:00.000', '2003-04-02T00:00:00.000')
If you parse a date-only value to an object that represents a timestamp, you are choosing to assign a point-in-time within that range. Thus, if you pick the very start of the range, you can easily shift into a different day when viewing that from another time zone.
Note that the JavaScript Date object is misnamed. It isn't a date, it's a timestamp.
A day.js object also represents a timestamp, as do most other libraries including Moment, Luxon, date-fns, and many others.
There are a few different solutions to this problem:
You can pick a time in the middle of the range which is less likely to be shifted to a different date when viewed from another time zone. For example, 12:00:00 noon. (Though this isn't perfect, as there are some time zones that go up to UTC+14.)
You can avoid treating a date as a timestamp, by keeping it in an object or string that represents it as a whole date.
Unfortunately, this isn't a concept that has caught on well in JavaScript yet. The language and most libraries do not handle it this way. (One notable exception is js-joda, which has a LocalDate data type.) However, this will eventually be coming to the JavaScript language itself via the Temporal proposal, which adds Temporal.PlainDate.
You can ignore the time portion of a timestamp and only look at the date part, but this only works if you lock all your operations to UTC rather than local time. In other words, treat '2003-04-01' as if it were '2003-04-01T00:00:00.000Z' and never convert it to local time or another time zone.
If you were using just the JavaScript Date object, then you would do:
const d = new Date('2003-04-01T00:00:00.000Z'); // the Z parses as UTC
const s = d.toISOString(); // this always emits UTC
But since you have a custom date format to parse and want to use day.js, you can do something like the following:
Define a function to work around day.js's parsing case sensitivity issue. (You need Apr, not APR.)
const fixMonthName = (s) => s.replace(/[A-Z]{2}-/, (m) => m.toLowerCase());
Parse the input string using day.js's UTC mode
const d = dayjs.utc(fixMonthName('01-APR-03'), 'DD-MMM-YY');
Get the output as a string however you would like, using any of day.js's display functions:
const s = d.toISOString(); // "2003-04-01T00:00:00.000Z"
// or
const s = d.format('YYYY-MM-DD'); // "2003-04-01"
Note that if you need a JavaScript Date object, do not use $d but instead call .toDate(). From there, make sure you are only using the UTC representation of the Date object. Keep in mind that while some environments will emit UTC when logging a Date object to the console (as if you called .toISOString(), other environments will emit the local time equivalent (as if you called .toString().

MongoDb: Best way to store time range

I need to store something like startTime and endTime in my document. To give some more context, these will reflect the opening and closing times for a shop. So, for example, startTime could be 9AM and endTime could be 9PM. What is the best way to store this? This is what I am doing right now:
timings: {
startTime: {
type: String,
required: [true, "....."]
},
endTime: {
type: String,
required: [true, "....."]
}
}
The idea is to store the values as strings ("9AM", "9PM") and do some sort of time parsing each time I query the database. But I was wondering if there was a better approach to this? Another idea I had is to store it as DateTime and ignore the date part. What else can I do? I'd like to avoid parsing/processing on application level as much as possible and leverage the power of mongodb.
I'm using mongoose and nodeJS.
I would agree the Date type is not relevant (and it has something to do with time zones and you might not want to get there ...)
I would store it as a number, not a string. Why ? because you might want to query it (like all "give me all shops that opens after 8pm"), and doing it with a string will be annoying ...
I'd go with that :
{
startTime: {
value: number;
amOrPm: string; //(if you don't want to use a 24 hours base)
},
endTime: {
value: number;
amOrPm: string; //(if you don't want to use a 24 hours base)
},
timeOffset: number // So you keep track on the offset with the base timezone
}
You could also store the minutes, or even store the time only in "minutes ellapsed since midnight" and convert it every time there is an access.
Having the offset this way won't allow you to easily query for a specific moment across different timezones, but I guess it's totally useless in your case.
Also you could store days as a number ('officially' sunday is 0, then monday is 1), but nowadays it is as easy to store a name so well ...
Edit: for the days maybe it's better to go with an array :
{
daysOppenned: [0, 1, 4]
}
And finally, what if each day has a different time openning ? Maybe you would have to consider having an array of days, each containing the time openning and time of closing, like above.
If you want to get even more into details, some shops are closed in middays and some other (like restaurants) only opens two times a day, you could then offer them to tick cases on a schedule and store that in an array.
Let us know if you need to build somthing like that !

Momentjs timezone - getting date at time in specific timezone

I am attempting to create a date from a UTC base in a user's specific timezone, in this case "America/Los Angeles" using momentjs/momentjs timezone. However, I'm not getting the results I expect:
var tempDate = moment(1448841600000); //moment("2015-11-30"); //monday 11/30 in UTC
var adjustedStart = moment.tz(tempDate, "America/Los_Angeles");
adjustedStart.hour(9);
adjustedStart.minute(30);
console.log("adjustedStart in milliseconds:" + adjustedStart.valueOf());
The console output is 1448875800000 which is 11/30/15 9:30AM in UTC/GMT, I was expecting 1448904600000 which is 11/30/15 9:30AM in Pacific Coast time. How can I translate this start date to the right time in the user's timezone?
Yes, 1448841600000 is the date you said:
moment(1448841600000).utc().format()
// "2015-11-30T00:00:00+00:00"
But that is a day earlier in Pacific time
moment(1448841600000).tz('America/Los_Angeles').format()
// "2015-11-29T16:00:00-08:00"
When you adjust it to 9:30 pacific, it's on the 29th, not the 30th.
moment(1448841600000).tz('America/Los_Angeles').hour(9).minute(30).format()
// "2015-11-29T09:30:00-08:00"
When you call valueOf, the result is:
moment(1448841600000).tz('America/Los_Angeles').hour(9).minute(30).valueOf()
// 1448818200000
This is the correct value, however it's different than the one you provided. However, it is indeed what I get when I run your code as well.
Screenshot from Chrome debug window, with your exact code:
Also, in comments you wrote:
//moment("2015-11-30"); //monday 11/30 in UTC
Actually, that would be in local time, not UTC. If you wanted UTC, you'd use:
moment.utc("2015-11-30")
Though it's unclear to me whether you are using this string input or the numeric timestamp.
If what you are asking is that you want the UTC date to be treated as if it were local and then have an arbitrary local time applied - that is a somewhat strange operation, but it would go something like this:
var tempDate = moment.utc(1448841600000);
var adjustedStart = moment.tz([tempDate.year(), tempDate.month(), tempDate.date(), 9, 30],
"America/Los_Angeles");
console.log("adjustedStart in milliseconds:" + adjustedStart.valueOf());
// adjustedStart in milliseconds:1448904600000
This gives the value you asked for, but to me - this is a smell that something is wrong with the expectation. I'd look a lot closer at the requirement and the other parts of the system.
From http://momentjs.com/docs/:
moment#valueOf simply outputs the number of milliseconds since the Unix Epoch
It is important to note that though the displays differ above, they are both the same moment in time.
var a = moment();
var b = moment.utc();
a.format(); // 2013-02-04T10:35:24-08:00
b.format(); // 2013-02-04T18:35:24+00:00
a.valueOf(); // 1360002924000
b.valueOf(); // 1360002924000
So it shouldn't differ for different timezones.
You should use adjustedStart.format(); to see the difference

Comparing Time Strings in Swift

In the app I am making when the view loads I want an object to be deleted in the background whenever the current time is greater than the given time. Otherwise if the current time is less than the given time the object loads normally. This is working fine for any time within the hour for example if the current time is 9:30 PM and the given time is 9:45 PM it works fine, but if the current time is 9:30 PM and the given time is 11:45 PM for some reason it doesn't know how to compare the hour so it doesn't work. Here is my code:
if timeString > End {
self.SpotterMap.removeAnnotation(SpotAnnotation)
let endTime = End
let query:PFQuery = PFQuery(className: "SpotInfo")
query.whereKey("spotendtime", equalTo: endTime)
query.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if let objects = objects {
for object in objects {
object.deleteInBackground()
}
}
})
print ("spot removed")
}
Can anyone give a solution to my problem?
Thanks
If you are going to work with times, you need to work with the NSDate type representation of your dates for the best accuracy, not a string one.
You will need to change your parse class spotInfo and add a column of type date, lets say you call it "realSpotEndTime".
To compare the times use .compare directly on the NSDates, if the dates return true when ordered ascending, it means that the first date comes before the second one. So your if statement would become;
(note: to get the real current time, you need to instantiate NSDate() right before comparison)
If this returns true and executes it means that the current time on the phone is less compared to the End time, remember, End needs to be in NSDate type as well, not string
let currentTime = NSDate()
//currentTime < End
if (currentTime.compare(End) == .OrderedAscending) {
...
query.whereKey("realSpotEndTime", lessThanOrEqualTo: currentTime)
}
or, alternatively you could do check if the current time is past the end time by
//End < currentTime
if (End.compare(currentTime) == .OrderedAscending) {
// fix parse query here
}

Problem with application reliant on British Summer Time in Azure

Part of an on premise app I am moving to the cloud, displays TV Scheduling information from a json source. The core data uses an offset in seconds from a start date to get it's start times which is all fine as these are all int UTC format.
The problem arises in the movement to the cloud.
The on premise app was situated in the UK so the locale is UntedKingdom and the TV times were correctly output using
return programmeStart.AddHours(programmeStart.IsDaylightSavingTime() ? 1 : 0);
However, having now moved to the cloud, the functionality for IsDaylightSavingTime, no longer returns true due to data centers being BST Agnostic.
Been racking my brains for a way to try and sort this.
Is there a quick and easy way to set what locale your hosted service runs under in Azure, or is the best solution to create an extension method that reads the boundries of when BST runs from and to, and then return true or false from there for example
public static class DateTimeExtension
{
public static bool IsMyDaylightSavingTime(this DateTime timeToTest)
{
if(timeToTest >= GetConfig("bstStart") && timeToTest <= GetConfig("bstFinish"))
{
return true;
}
return false;
}
}
And the maintaing the config values of bst changing as they move?
Thanks
You can't change the timezone of the Azure servers - there's lots going on that assumes UTC is the current setting.
You should be able to get hold of the UK timezoneinfo by string, e.g.:
TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
After you've done that, then you can use the framework method TimeZoneInfo.IsDaylightSavingTime http://msdn.microsoft.com/en-us/library/bb460642.aspx
tzi.IsDaylightSavingTime(DateTime.Now);
Why can't you simply return UTC and let the client translate that per their locale?
Edit: Here is code
var offset = TimeZoneInfo.GetSystemTimeZones()
.Where(z => z.Id == "GMT Standard Time")
.Single()
.GetUtcOffset(DateTime.UtcNow)

Resources