Formatting local timestamps with WinRT - datetime

WinRT uses the DateTimeFormatter class to turn timestamps into human-readable dates. In C++CX, you'll pass it a DateTime instance, which contains a timestamp in UTC time, and let it work its magic.
However, I have an application that consumes timestamps in local time. I'd like to format them and show them to my users, but if I pass the timestamp as is, the DateTimeFormatter will assume that it's UTC and will try to convert it to local time again, resulting in incorrect times.
How can I display local time with WinRT? Is there a way to turn back local time into UTC time?
The timestamps are generated from the machine that consumes them, so there is no risk of timezone confusion. It would also be technically feasible to produce UTC timestamps instead, but this would be rather inconvenient and I'd like to fall back to that only if it's the only way.

Thankfully, FileTimeToSystemTime, TzSpecificLocalTimeToSystemTime and SystemTimeToFileTime are all available to Windows store apps. With that, it's possible to create a function to change local back to UTC.
uint64 LocalTimeToUtcTime(uint64 local)
{
LARGE_INTEGER largeTime;
largeTime.QuadPart = local;
FILETIME intermediate;
intermediate.dwHighDateTime = largeTime.HighPart;
intermediate.dwLowDateTime = largeTime.LowPart;
SYSTEMTIME systemLocal, systemUtc;
if (!FileTimeToSystemTime(&intermediate, &systemLocal))
{
// handle error
}
if (!TzSpecificLocalTimeToSystemTime(nullptr, &systemLocal, &systemUtc))
{
// handle error
}
if (!SystemTimeToFileTime(&systemUtc, &intermediate))
{
// handle error
}
largeTime.HighPart = intermediate.dwHighDateTime;
largeTime.LowPart = intermediate.dwLowDateTime;
return largeTime.QuadPart;
}

You can use the Windows::Globalization::Calendar class to work with local time, or with time in any time zone.
The Calendar defaults to the local time zone if you don't explicitly set one. You can then use GetDateTime() to retrieve a Windows::Foundation::DateTime instance that can be used with DateTimeFormatter.
Calendar^ cal = ref new Calendar();
cal->SetToMin();
cal->Year = 2014;
cal->Month = 7;
cal->Day = 14;
cal->Hour = 12;
cal->Minute = 34;
cal->Second = 56;
DateTime dt = cal->GetDateTime();
DateTimeFormatter^ dtf = ref new DateTimeFormatter("shortdate shorttime");
String^ result = dtf->Format(dt);
Logger::WriteMessage(result->Data());

Related

Luxon setZone not working when getting epoch time (valueOf)

I have been trying to use Luxon to get (for comparison reasons) the epoch miliseconds from two DateTimes in different timezone.
I'm using the .valueOf() method to do so, but even that I'm setting the different timezones for each DateTime, that method always return the same value:
const { DateTime } = require('luxon')
const dtString = '2022-01-15 13:00:00'
const d1 = DateTime.fromSQL(dtString).setZone('America/Sao_Paulo').valueOf()
const d2 = DateTime.fromSQL(dtString).setZone('America/New_York').valueOf()
console.log(d1) // logs 1642262400000
console.log(d2) // logs 1642262400000 (same)
What is wrong?
Instead of parsing the dates in one zone and then shifting the times around, just parse them in the zone you want:
const dtString = '2022-01-15 13:00:00';
const d1 = DateTime.fromSQL(dtString, { zone: "America/Sao_Paulo" }).valueOf();
const d2 = DateTime.fromSQL(dtString, { zone: "America/New_York" }).valueOf();
d1 //=> 1642262400000
d2 //=> 1642269600000
The difference there is that in yours, Luxon parses the string using your system zone. Then you change the zone, but the time is still the time, unless you use keepLocalTime. In mine, I'm telling Luxon what zone to use in interpreting the string, which results in a different time being computed for the two cases.
Based on this Luxon issue, I found the fix for that:
We should use the keepCalendarTime: true option in the setZone method to make it work.
That extra flag says "yes, change the actual time"
So the functional code looks like:
const { DateTime } = require('luxon')
const dtString = '2022-01-15 13:00:00'
const opts = { keepCalendarTime: true }
const d1 = DateTime.fromSQL(dtString).setZone('America/Sao_Paulo', opts).valueOf()
const d2 = DateTime.fromSQL(dtString).setZone('America/New_York', opts).valueOf()
console.log(d1) // logs 1642262400000
console.log(d2) // logs 1642269600000 (prints different value)

SwiftUI or Combine Clock/Timer events

Using SwiftUI (or Combine) how might I set up a series of one or more events that are triggered by the (system) clock. Examples might include:
Every night at midnight,
On the hour,
Every fifteen minutes on the quarter hour,
Finally, on a slightly different note: On the 29th of February 2020 at 12:15.
An approximation is easily achieved by setting up a timer event that fires every second and then checking the hours/minutes/seconds, etc. but this seems very inefficient for events that may be many hours or days apart.
I'm looking for something that is closely synchronised to the actual system clock and fires off a single event at the required time rather than firing loads of events and having each one ask "Are we there yet?".
I would suggest the following:
DispatchQueue.global(qos: .background).async {
let isoDate = "2020-01-13T16:58:30+0000"
let dateFormatter = ISO8601DateFormatter()
let date = dateFormatter.date(from:isoDate)!
let t = Timer(fire: date, interval: 2, repeats: true) { timer in
print("fired")
}
let runLoop = RunLoop.current
runLoop.add(t, forMode: .default)
runLoop.run()
}
string to date conversion I used this answer to format the time correctly.
The example is in GMT.
documentation apple you can look up timer tolerance which can be adjusted if you need the timer to be very accurate.
interval is in seconds so this solution won't get more accurate than seconds
You might want to enable the Background Modes capability to go for the very long running timers. Never done that so I can't help here.
All your examples should work. I hope this helps!
I had to implement this feature too using Combine / SwiftUI : a Timer that would execute at start then every day, hour or minutes (for testing), here is my solution if it can be useful or improved :)
class PeriodicPublisher {
var periodicFormat: PeriodicFormat = .daily
init(_ format: PeriodicFormat = .daily) {
self.periodicFormat = format
}
// Must have an equatable for removeDuplicate
struct OutputDate: Equatable {
let compared: String
let original: String
init(_ comparedDatePart: String, _ originalDate: String) {
self.compared = comparedDatePart
self.original = originalDate
}
static func ==(lhs: OutputDate, rhs: OutputDate) -> Bool {
return lhs.compared == rhs.compared
}
}
enum PeriodicFormat {
case daily
case hourly
case minutely
func toComparableDate() -> String {
switch self {
case .daily:
return "yyyy-MM-dd"
case .hourly:
return "HH"
case .minutely:
return "mm"
}
}
}
func getPublisher() -> AnyPublisher<OutputDate, Never> {
let compareDateFormatter = DateFormatter()
compareDateFormatter.dateFormat = self.periodicFormat.toComparableDate()
let originalTimerDateFormatter = DateFormatter()
originalTimerDateFormatter.dateFormat = "yyyy-MM-dd HH:mm"
var nowDate: Just<OutputDate> {
let comparedDate = compareDateFormatter.string(from: Date())
let originalDate = originalTimerDateFormatter.string(from: Date())
return Just(OutputDate(comparedDate, originalDate))
}
let timerDate = Timer.publish(every: 2.0, tolerance: 1.0, on: .main, in: .default, options: nil)
.autoconnect()
.map { dateString -> OutputDate in
return OutputDate(compareDateFormatter.string(from: dateString), originalTimerDateFormatter.string(from: dateString))
}
.eraseToAnyPublisher()
return Publishers.Merge(nowDate, timerDate)
.map { $0 }
.removeDuplicates()
.eraseToAnyPublisher()
}
}
How does it work ?
Every 2 seconds the scheduler issue current date (with Timer.publish()), this date is used to create a "OutputDate" holding two properties : one "comparable" part used to compare if something has changed and one "original" part so it can be useful for the consumer.
Comparable property is Timer's date formatted with toComparableDate given the provided configuration (.daily, .hourly, .minutely). Using "removeDuplicates" on this property allow to publish "OutputDate" only when this value changes. Every day or hour or minute.
Publishers.Merge is used to publish a value immediately after instantiation, otherwise nothing happens before the first Timer.publish(every). Here 2 seconds.
How to use it ?
You would use it with Combine like this :
PeriodicPublisher(.daily).getPublisher().sink { date in
print("Day has changed \(date.original)")
}

DocumentDB Change Feed and saving Checkpoint

After reading the documentation, I'm having a hard time conceptualizing the change feed. Let's take the code from the documentation below. The second change feed is picking up the changes from the last time it was run via the checkpoints. Let's say it is being used to create summary data and there was an issue and it needed to be re-run from a prior time. I don't understand the following:
How to specify a particular time the checkpoint should start. I understand I can save the checkpoint dictionary and use that for each run, but how do you get the changes from X time to maybe rerun some summary data
Secondly, let's say we are rerunning some summary data and we save the last checkpoint used for each summarized data so we know where that one left off. How does one know that a record is in or before that checkpoint?
Code that runs from collection beginning and then from last checkpoint:
Dictionary < string, string > checkpoints = await GetChanges(client, collection, new Dictionary < string, string > ());
await client.CreateDocumentAsync(collection, new DeviceReading {
DeviceId = "xsensr-201", MetricType = "Temperature", Unit = "Celsius", MetricValue = 1000
});
await client.CreateDocumentAsync(collection, new DeviceReading {
DeviceId = "xsensr-212", MetricType = "Pressure", Unit = "psi", MetricValue = 1000
});
// Returns only the two documents created above.
checkpoints = await GetChanges(client, collection, checkpoints);
//
private async Task < Dictionary < string, string >> GetChanges(
DocumentClient client,
string collection,
Dictionary < string, string > checkpoints) {
List < PartitionKeyRange > partitionKeyRanges = new List < PartitionKeyRange > ();
FeedResponse < PartitionKeyRange > pkRangesResponse;
do {
pkRangesResponse = await client.ReadPartitionKeyRangeFeedAsync(collection);
partitionKeyRanges.AddRange(pkRangesResponse);
}
while (pkRangesResponse.ResponseContinuation != null);
foreach(PartitionKeyRange pkRange in partitionKeyRanges) {
string continuation = null;
checkpoints.TryGetValue(pkRange.Id, out continuation);
IDocumentQuery < Document > query = client.CreateDocumentChangeFeedQuery(
collection,
new ChangeFeedOptions {
PartitionKeyRangeId = pkRange.Id,
StartFromBeginning = true,
RequestContinuation = continuation,
MaxItemCount = 1
});
while (query.HasMoreResults) {
FeedResponse < DeviceReading > readChangesResponse = query.ExecuteNextAsync < DeviceReading > ().Result;
foreach(DeviceReading changedDocument in readChangesResponse) {
Console.WriteLine(changedDocument.Id);
}
checkpoints[pkRange.Id] = readChangesResponse.ResponseContinuation;
}
}
return checkpoints;
}
DocumentDB supports check-pointing only by the logical timestamp returned by the server. If you would like to retrieve all changes from X minutes ago, you would have to "remember" the logical timestamp corresponding to the clock time (ETag returned for the collection in the REST API, ResponseContinuation in the SDK), then use that to retrieve changes.
Change feed uses logical time in place of clock time because it can be different across various servers/partitions. If you would like to see change feed support based on clock time (with some caveats on skew), please propose/upvote at https://feedback.azure.com/forums/263030-documentdb/.
To save the last checkpoint per partition key/document, you can just save the corresponding version of the batch in which it was last seen (ETag returned for the collection in the REST API, ResponseContinuation in the SDK), like Fred suggested in his answer.
How to specify a particular time the checkpoint should start.
You could try to provide a logical version/ETag (such as 95488) instead of providing a null value as RequestContinuation property of ChangeFeedOptions.

How to compare two dates with each other in Flex?

I have a standard ISO8601 date string:
2004-02-12T15:19:21+00:00
I want to know if this date is older than 10 minutes ago in Flex. So basically:
if ("2004-02-12T15:19:21+00:00" > current_time - 10mins) {
// do whatever
}
What would the syntax be in Flex? I'm basically stuck at trying to convert the string into a Flex Date Object without parsing it character by character.
If you don;t care about the timezone i.e. the date string is in the same timezone as where you are running the application then this should work.
var date:Date = DateFormatter.parseDateString("2004-02-12T15:19:21+00:00");
var now:Date = new Date();
var tenMinAgo:Number = now.time - 1000*60*10;
if (date.time < tenMinAgo) {
trace("More than 10 min ago");
}

How to parse time in Salesforce Visualforce, Apex?

I'm quite new to visualforce/apex programming and kinda stuck in parsing current time and time functions in apex.
Basically I'm trying to figure out if the current time is within my given time range.
How do I write this in apex language?
public String isWorkhours;
public String getIsWorkhours() {
Datetime current_time = *(get current time)*
Datetime start_time = *(7:00 am)*
Datetime end_time = *(7:00 pm)*
if ((current_time >= start_time) && (current_time <= end_time)) {
isWorkhours= 'yes';
} else {
isWorkhours= 'no';
}
return isWorkhours;
}
First of all - check if you can use Business Hours (standard object) to configure SF instead of writing similar functionality manually. Just check your online help for these.
Example of comparison of 2 datetime objects: http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_classes_businesshours.htm
Secondly - read the reference of Time class. Something like this?
Time start = Time.newInstance(7,0,0,0), stop = Time.newInstance(19,0,0,0);
DateTime now = System.now(); // or DateTime.now();
System.debug(now.time() > start && now.time() < stop);

Resources